From 7c013ced92c4cd8d0f91512d08b946dfc6457ffb Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 06:01:23 -0500 Subject: [PATCH 01/12] Removed unnecessary comment Signed-off-by: Whit Waldo --- src/Dapr.Jobs/JsonConverters/Iso8601DateTimeJsonConverter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Dapr.Jobs/JsonConverters/Iso8601DateTimeJsonConverter.cs b/src/Dapr.Jobs/JsonConverters/Iso8601DateTimeJsonConverter.cs index 90ffd3d4d..d0198d8fd 100644 --- a/src/Dapr.Jobs/JsonConverters/Iso8601DateTimeJsonConverter.cs +++ b/src/Dapr.Jobs/JsonConverters/Iso8601DateTimeJsonConverter.cs @@ -17,8 +17,7 @@ namespace Dapr.Jobs.JsonConverters; /// -/// Converts from an ISO 8601 DateTime to a string and back. This is primarily used to serialize -/// dates for use with CosmosDB. +/// Converts from an ISO 8601 DateTime to a string and back. /// public sealed class Iso8601DateTimeJsonConverter : JsonConverter { From 86b89bd9421937de1e78cc7a8b80a095249aac7a Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 06:06:52 -0500 Subject: [PATCH 02/12] Update to use file-level namespacing Signed-off-by: Whit Waldo --- src/Dapr.Common/ArgumentVerifier.cs | 63 ++++++++++++++--------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/Dapr.Common/ArgumentVerifier.cs b/src/Dapr.Common/ArgumentVerifier.cs index 62ae98b54..b7f142132 100644 --- a/src/Dapr.Common/ArgumentVerifier.cs +++ b/src/Dapr.Common/ArgumentVerifier.cs @@ -14,47 +14,46 @@ // TODO: Remove this when every project that uses this file has nullable enabled. #nullable enable -namespace Dapr -{ - using System; - using System.Diagnostics.CodeAnalysis; +namespace Dapr; + +using System; +using System.Diagnostics.CodeAnalysis; +/// +/// A utility class to perform argument validations. +/// +internal static class ArgumentVerifier +{ /// - /// A utility class to perform argument validations. + /// Throws ArgumentNullException if argument is null. /// - internal static class ArgumentVerifier + /// Argument value to check. + /// Name of Argument. + public static void ThrowIfNull([NotNull] object? value, string name) { - /// - /// Throws ArgumentNullException if argument is null. - /// - /// Argument value to check. - /// Name of Argument. - public static void ThrowIfNull([NotNull] object? value, string name) + if (value == null) { - if (value == null) - { - throw new ArgumentNullException(name); - } + throw new ArgumentNullException(name); } + } - /// - /// Validates string and throws: - /// ArgumentNullException if argument is null. - /// ArgumentException if argument is empty. - /// - /// Argument value to check. - /// Name of Argument. - public static void ThrowIfNullOrEmpty([NotNull] string? value, string name) + /// + /// Validates string and throws: + /// ArgumentNullException if argument is null. + /// ArgumentException if argument is empty. + /// + /// Argument value to check. + /// Name of Argument. + public static void ThrowIfNullOrEmpty([NotNull] string? value, string name) + { + if (value == null) { - if (value == null) - { - throw new ArgumentNullException(name); - } + throw new ArgumentNullException(name); + } - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("The value cannot be null or empty", name); - } + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentException("The value cannot be null or empty", name); } } } From 63f5ad0b64b5ef0fc5d24ed3125b0d3a35f36c97 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 06:08:14 -0500 Subject: [PATCH 03/12] Update to use file-level namespaces Signed-off-by: Whit Waldo --- src/Dapr.Common/DaprDefaults.cs | 199 +++++++------- src/Dapr.Common/DaprException.cs | 61 ++--- .../Exceptions/DaprExceptionExtensions.cs | 55 ++-- .../Exceptions/DaprExtendedErrorConstants.cs | 39 ++- .../Exceptions/DaprExtendedErrorDetail.cs | 256 +++++++++--------- .../Exceptions/DaprExtendedErrorInfo.cs | 23 +- .../Exceptions/DaprExtendedErrorType.cs | 127 +++++---- .../Exceptions/ExtendedErrorDetailFactory.cs | 169 ++++++------ 8 files changed, 460 insertions(+), 469 deletions(-) diff --git a/src/Dapr.Common/DaprDefaults.cs b/src/Dapr.Common/DaprDefaults.cs index 2dd8dd378..9a504986d 100644 --- a/src/Dapr.Common/DaprDefaults.cs +++ b/src/Dapr.Common/DaprDefaults.cs @@ -13,122 +13,121 @@ using Microsoft.Extensions.Configuration; -namespace Dapr +namespace Dapr; + +internal static class DaprDefaults { - internal static class DaprDefaults - { - private static string httpEndpoint = string.Empty; - private static string grpcEndpoint = string.Empty; - private static string daprApiToken = string.Empty; - private static string appApiToken = string.Empty; + private static string httpEndpoint = string.Empty; + private static string grpcEndpoint = string.Empty; + private static string daprApiToken = string.Empty; + private static string appApiToken = string.Empty; - public const string DaprApiTokenName = "DAPR_API_TOKEN"; - public const string AppApiTokenName = "APP_API_TOKEN"; - public const string DaprHttpEndpointName = "DAPR_HTTP_ENDPOINT"; - public const string DaprHttpPortName = "DAPR_HTTP_PORT"; - public const string DaprGrpcEndpointName = "DAPR_GRPC_ENDPOINT"; - public const string DaprGrpcPortName = "DAPR_GRPC_PORT"; + public const string DaprApiTokenName = "DAPR_API_TOKEN"; + public const string AppApiTokenName = "APP_API_TOKEN"; + public const string DaprHttpEndpointName = "DAPR_HTTP_ENDPOINT"; + public const string DaprHttpPortName = "DAPR_HTTP_PORT"; + public const string DaprGrpcEndpointName = "DAPR_GRPC_ENDPOINT"; + public const string DaprGrpcPortName = "DAPR_GRPC_PORT"; - public const string DefaultDaprScheme = "http"; - public const string DefaultDaprHost = "localhost"; - public const int DefaultHttpPort = 3500; - public const int DefaultGrpcPort = 50001; + public const string DefaultDaprScheme = "http"; + public const string DefaultDaprHost = "localhost"; + public const int DefaultHttpPort = 3500; + public const int DefaultGrpcPort = 50001; - /// - /// Get the value of environment variable DAPR_API_TOKEN - /// - /// The optional to pull the value from. - /// The value of environment variable DAPR_API_TOKEN - public static string GetDefaultDaprApiToken(IConfiguration? configuration) => - GetResourceValue(configuration, DaprApiTokenName) ?? string.Empty; + /// + /// Get the value of environment variable DAPR_API_TOKEN + /// + /// The optional to pull the value from. + /// The value of environment variable DAPR_API_TOKEN + public static string GetDefaultDaprApiToken(IConfiguration? configuration) => + GetResourceValue(configuration, DaprApiTokenName) ?? string.Empty; - /// - /// Get the value of environment variable APP_API_TOKEN - /// - /// The optional to pull the value from. - /// The value of environment variable APP_API_TOKEN - public static string GetDefaultAppApiToken(IConfiguration? configuration) => - GetResourceValue(configuration, AppApiTokenName) ?? string.Empty; + /// + /// Get the value of environment variable APP_API_TOKEN + /// + /// The optional to pull the value from. + /// The value of environment variable APP_API_TOKEN + public static string GetDefaultAppApiToken(IConfiguration? configuration) => + GetResourceValue(configuration, AppApiTokenName) ?? string.Empty; - /// - /// Get the value of HTTP endpoint based off environment variables - /// - /// The optional to pull the value from. - /// The value of HTTP endpoint based off environment variables - public static string GetDefaultHttpEndpoint(IConfiguration? configuration = null) - { - //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated - var endpoint = GetResourceValue(configuration, DaprHttpEndpointName); - var port = GetResourceValue(configuration, DaprHttpPortName); + /// + /// Get the value of HTTP endpoint based off environment variables + /// + /// The optional to pull the value from. + /// The value of HTTP endpoint based off environment variables + public static string GetDefaultHttpEndpoint(IConfiguration? configuration = null) + { + //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated + var endpoint = GetResourceValue(configuration, DaprHttpEndpointName); + var port = GetResourceValue(configuration, DaprHttpPortName); - //Use the default HTTP port if we're unable to retrieve/parse the provided port - int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultHttpPort : int.Parse(port); + //Use the default HTTP port if we're unable to retrieve/parse the provided port + int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultHttpPort : int.Parse(port); - return BuildEndpoint(endpoint, parsedGrpcPort.Value); - } + return BuildEndpoint(endpoint, parsedGrpcPort.Value); + } - /// - /// Get the value of gRPC endpoint based off environment variables - /// - /// The optional to pull the value from. - /// The value of gRPC endpoint based off environment variables - public static string GetDefaultGrpcEndpoint(IConfiguration? configuration = null) - { - //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated - var endpoint = GetResourceValue(configuration, DaprGrpcEndpointName); - var port = GetResourceValue(configuration, DaprGrpcPortName); + /// + /// Get the value of gRPC endpoint based off environment variables + /// + /// The optional to pull the value from. + /// The value of gRPC endpoint based off environment variables + public static string GetDefaultGrpcEndpoint(IConfiguration? configuration = null) + { + //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated + var endpoint = GetResourceValue(configuration, DaprGrpcEndpointName); + var port = GetResourceValue(configuration, DaprGrpcPortName); - //Use the default gRPC port if we're unable to retrieve/parse the provided port - int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultGrpcPort : int.Parse(port); + //Use the default gRPC port if we're unable to retrieve/parse the provided port + int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultGrpcPort : int.Parse(port); - return BuildEndpoint(endpoint, parsedGrpcPort.Value); - } - - /// - /// Builds the Dapr endpoint. - /// - /// The endpoint value. - /// The endpoint port value, whether pulled from configuration/envvar or the default. - /// A constructed endpoint value. - private static string BuildEndpoint(string? endpoint, int endpointPort) - { - var endpointBuilder = new UriBuilder { Scheme = DefaultDaprScheme, Host = DefaultDaprHost }; //Port depends on endpoint + return BuildEndpoint(endpoint, parsedGrpcPort.Value); + } - if (!string.IsNullOrWhiteSpace(endpoint)) //If the endpoint is set, it doesn't matter if the port is - { - //Extract the scheme, host and port from the endpoint and replace defaults - var uri = new Uri(endpoint); - endpointBuilder.Scheme = uri.Scheme; - endpointBuilder.Host = uri.Host; - endpointBuilder.Port = uri.Port; - } - else - { - //Should only set the port if the endpoint isn't populated - endpointBuilder.Port = endpointPort; - } + /// + /// Builds the Dapr endpoint. + /// + /// The endpoint value. + /// The endpoint port value, whether pulled from configuration/envvar or the default. + /// A constructed endpoint value. + private static string BuildEndpoint(string? endpoint, int endpointPort) + { + var endpointBuilder = new UriBuilder { Scheme = DefaultDaprScheme, Host = DefaultDaprHost }; //Port depends on endpoint - return endpointBuilder.ToString(); + if (!string.IsNullOrWhiteSpace(endpoint)) //If the endpoint is set, it doesn't matter if the port is + { + //Extract the scheme, host and port from the endpoint and replace defaults + var uri = new Uri(endpoint); + endpointBuilder.Scheme = uri.Scheme; + endpointBuilder.Host = uri.Host; + endpointBuilder.Port = uri.Port; } - - /// - /// Retrieves the specified value prioritizing pulling it from , falling back - /// to an environment variable, and using an empty string as a default. - /// - /// An instance of an . - /// The name of the value to retrieve. - /// The value of the resource. - private static string? GetResourceValue(IConfiguration? configuration, string name) + else { - //Attempt to retrieve first from the configuration - var configurationValue = configuration?[name]; - if (configurationValue is not null) - { - return configurationValue; - } + //Should only set the port if the endpoint isn't populated + endpointBuilder.Port = endpointPort; + } - //Fall back to the environment variable with the same name or default to an empty string - return Environment.GetEnvironmentVariable(name); + return endpointBuilder.ToString(); + } + + /// + /// Retrieves the specified value prioritizing pulling it from , falling back + /// to an environment variable, and using an empty string as a default. + /// + /// An instance of an . + /// The name of the value to retrieve. + /// The value of the resource. + private static string? GetResourceValue(IConfiguration? configuration, string name) + { + //Attempt to retrieve first from the configuration + var configurationValue = configuration?[name]; + if (configurationValue is not null) + { + return configurationValue; } + + //Fall back to the environment variable with the same name or default to an empty string + return Environment.GetEnvironmentVariable(name); } } diff --git a/src/Dapr.Common/DaprException.cs b/src/Dapr.Common/DaprException.cs index 2b600ef3a..d2cedea7d 100644 --- a/src/Dapr.Common/DaprException.cs +++ b/src/Dapr.Common/DaprException.cs @@ -13,45 +13,44 @@ using System.Runtime.Serialization; -namespace Dapr +namespace Dapr; + +/// +/// The base type of exceptions thrown by the Dapr .NET SDK. +/// +[Serializable] +public class DaprException : Exception { /// - /// The base type of exceptions thrown by the Dapr .NET SDK. + /// Initializes a new instance of with the provided . /// - [Serializable] - public class DaprException : Exception + /// The exception message. + public DaprException(string message) + : base(message) { - /// - /// Initializes a new instance of with the provided . - /// - /// The exception message. - public DaprException(string message) - : base(message) - { - } + } - /// - /// Initializes a new instance of with the provided - /// and . - /// - /// The exception message. - /// The inner exception. - public DaprException(string message, Exception innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of with the provided + /// and . + /// + /// The exception message. + /// The inner exception. + public DaprException(string message, Exception innerException) + : base(message, innerException) + { + } - /// - /// Initializes a new instance of the class with a specified context. - /// - /// The object that contains serialized object data of the exception being thrown. - /// The object that contains contextual information about the source or destination. The context parameter is reserved for future use and can be null. + /// + /// Initializes a new instance of the class with a specified context. + /// + /// The object that contains serialized object data of the exception being thrown. + /// The object that contains contextual information about the source or destination. The context parameter is reserved for future use and can be null. #if NET8_0_OR_GREATER [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to GetObjectData #endif - protected DaprException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } + protected DaprException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } } diff --git a/src/Dapr.Common/Exceptions/DaprExceptionExtensions.cs b/src/Dapr.Common/Exceptions/DaprExceptionExtensions.cs index 2f195d692..e1bd3d8c8 100644 --- a/src/Dapr.Common/Exceptions/DaprExceptionExtensions.cs +++ b/src/Dapr.Common/Exceptions/DaprExceptionExtensions.cs @@ -14,42 +14,41 @@ using System.Diagnostics.CodeAnalysis; using Grpc.Core; -namespace Dapr.Common.Exceptions +namespace Dapr.Common.Exceptions; + +/// +/// Provides extension methods for . +/// +public static class DaprExceptionExtensions { /// - /// Provides extension methods for . + /// Attempt to retrieve from . /// - public static class DaprExceptionExtensions + /// A Dapr exception. . + /// out if parsable from inner exception, null otherwise. + /// True if extended info is available, false otherwise. + public static bool TryGetExtendedErrorInfo(this DaprException exception, [NotNullWhen(true)] out DaprExtendedErrorInfo? daprExtendedErrorInfo) { - /// - /// Attempt to retrieve from . - /// - /// A Dapr exception. . - /// out if parsable from inner exception, null otherwise. - /// True if extended info is available, false otherwise. - public static bool TryGetExtendedErrorInfo(this DaprException exception, [NotNullWhen(true)] out DaprExtendedErrorInfo? daprExtendedErrorInfo) + daprExtendedErrorInfo = null; + if (exception.InnerException is not RpcException rpcException) { - daprExtendedErrorInfo = null; - if (exception.InnerException is not RpcException rpcException) - { - return false; - } + return false; + } - var metadata = rpcException.Trailers.Get(DaprExtendedErrorConstants.GrpcDetails); + var metadata = rpcException.Trailers.Get(DaprExtendedErrorConstants.GrpcDetails); - if (metadata is null) - { - return false; - } + if (metadata is null) + { + return false; + } - var status = Google.Rpc.Status.Parser.ParseFrom(metadata.ValueBytes); + var status = Google.Rpc.Status.Parser.ParseFrom(metadata.ValueBytes); - daprExtendedErrorInfo = new DaprExtendedErrorInfo(status.Code, status.Message) - { - Details = status.Details.Select(detail => ExtendedErrorDetailFactory.CreateErrorDetail(detail)).ToArray(), - }; + daprExtendedErrorInfo = new DaprExtendedErrorInfo(status.Code, status.Message) + { + Details = status.Details.Select(detail => ExtendedErrorDetailFactory.CreateErrorDetail(detail)).ToArray(), + }; - return true; - } + return true; } -} +} \ No newline at end of file diff --git a/src/Dapr.Common/Exceptions/DaprExtendedErrorConstants.cs b/src/Dapr.Common/Exceptions/DaprExtendedErrorConstants.cs index 9507103bb..b846827de 100644 --- a/src/Dapr.Common/Exceptions/DaprExtendedErrorConstants.cs +++ b/src/Dapr.Common/Exceptions/DaprExtendedErrorConstants.cs @@ -11,24 +11,23 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Common.Exceptions +namespace Dapr.Common.Exceptions; + +/// +/// Definitions of expected types to be returned from the Dapr runtime. +/// +internal static class DaprExtendedErrorConstants { - /// - /// Definitions of expected types to be returned from the Dapr runtime. - /// - internal static class DaprExtendedErrorConstants - { - public const string ErrorDetailTypeUrl = "type.googleapis.com/"; - public const string GrpcDetails = "grpc-status-details-bin"; - public const string ErrorInfo = $"{ErrorDetailTypeUrl}Google.rpc.ErrorInfo"; - public const string RetryInfo = $"{ErrorDetailTypeUrl}Google.rpc.RetryInfo"; - public const string DebugInfo = $"{ErrorDetailTypeUrl}Google.rpc.DebugInfo"; - public const string QuotaFailure = $"{ErrorDetailTypeUrl}Google.rpc.QuotaFailure"; - public const string PreconditionFailure = $"{ErrorDetailTypeUrl}Google.rpc.PreconditionFailure"; - public const string BadRequest = $"{ErrorDetailTypeUrl}Google.rpc.BadRequest"; - public const string RequestInfo = $"{ErrorDetailTypeUrl}Google.rpc.RequestInfo"; - public const string ResourceInfo = $"{ErrorDetailTypeUrl}Google.rpc.ResourceInfo"; - public const string Help = $"{ErrorDetailTypeUrl}Google.rpc.Help"; - public const string LocalizedMessage = $"{ErrorDetailTypeUrl}Google.rpc.LocalizedMessage"; - } -} + public const string ErrorDetailTypeUrl = "type.googleapis.com/"; + public const string GrpcDetails = "grpc-status-details-bin"; + public const string ErrorInfo = $"{ErrorDetailTypeUrl}Google.rpc.ErrorInfo"; + public const string RetryInfo = $"{ErrorDetailTypeUrl}Google.rpc.RetryInfo"; + public const string DebugInfo = $"{ErrorDetailTypeUrl}Google.rpc.DebugInfo"; + public const string QuotaFailure = $"{ErrorDetailTypeUrl}Google.rpc.QuotaFailure"; + public const string PreconditionFailure = $"{ErrorDetailTypeUrl}Google.rpc.PreconditionFailure"; + public const string BadRequest = $"{ErrorDetailTypeUrl}Google.rpc.BadRequest"; + public const string RequestInfo = $"{ErrorDetailTypeUrl}Google.rpc.RequestInfo"; + public const string ResourceInfo = $"{ErrorDetailTypeUrl}Google.rpc.ResourceInfo"; + public const string Help = $"{ErrorDetailTypeUrl}Google.rpc.Help"; + public const string LocalizedMessage = $"{ErrorDetailTypeUrl}Google.rpc.LocalizedMessage"; +} \ No newline at end of file diff --git a/src/Dapr.Common/Exceptions/DaprExtendedErrorDetail.cs b/src/Dapr.Common/Exceptions/DaprExtendedErrorDetail.cs index 17c10c20b..0ed738c27 100644 --- a/src/Dapr.Common/Exceptions/DaprExtendedErrorDetail.cs +++ b/src/Dapr.Common/Exceptions/DaprExtendedErrorDetail.cs @@ -11,147 +11,145 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Common.Exceptions +namespace Dapr.Common.Exceptions; + +/// +/// Abstract base class of the Dapr extended error detail. +/// +public abstract record DaprExtendedErrorDetail(DaprExtendedErrorType ErrorType); + +/// +/// Detail when the type url is unrecognized. +/// +/// The unrecognized type url. +public sealed record DaprUnknownDetail(string TypeUrl) : DaprExtendedErrorDetail(DaprExtendedErrorType.Unknown); + +/// +/// Detail proving debugging information. +/// +/// Stack trace entries relating to error. +/// Further related debugging information. +public sealed record DaprDebugInfoDetail(IReadOnlyCollection StackEntries, string Detail) : DaprExtendedErrorDetail(DaprExtendedErrorType.DebugInfo); + +/// +/// A precondtion violation. +/// +/// The type of the violation. +/// The subject that the violation relates to. +/// A description of how the precondition may have failed. +public sealed record DaprPreconditionFailureViolation(string Type, string Subject, string Description); + +/// +/// Detail relating to a failed precondition e.g user has not completed some required check. +/// +public sealed record DaprPreconditionFailureDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.PreconditionFailure) { /// - /// Abstract base class of the Dapr extended error detail. + /// Collection of . /// - public abstract record DaprExtendedErrorDetail(DaprExtendedErrorType ErrorType); + public IReadOnlyCollection Violations { get; init; } = Array.Empty(); +} - /// - /// Detail when the type url is unrecognized. - /// - /// The unrecognized type url. - public sealed record DaprUnknownDetail(string TypeUrl) : DaprExtendedErrorDetail(DaprExtendedErrorType.Unknown); - - /// - /// Detail proving debugging information. - /// - /// Stack trace entries relating to error. - /// Further related debugging information. - public sealed record DaprDebugInfoDetail(IReadOnlyCollection StackEntries, string Detail) : DaprExtendedErrorDetail(DaprExtendedErrorType.DebugInfo); - - /// - /// A precondtion violation. - /// - /// The type of the violation. - /// The subject that the violation relates to. - /// A description of how the precondition may have failed. - public sealed record DaprPreconditionFailureViolation(string Type, string Subject, string Description); - - /// - /// Detail relating to a failed precondition e.g user has not completed some required check. - /// - public sealed record DaprPreconditionFailureDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.PreconditionFailure) - { - /// - /// Collection of . - /// - public IReadOnlyCollection Violations { get; init; } = Array.Empty(); - } - - /// - /// Provides the time offset the client should use before retrying. - /// - /// Second offset. - /// Nano offset. - public sealed record DaprRetryDelay(long Seconds, int Nanos); - - /// - /// Detail containing retry information. Provides the minimum amount of the time the client should wait before retrying a request. - /// - public sealed record DaprRetryInfoDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.RetryInfo) - { - /// - /// A . - /// - public DaprRetryDelay Delay = new(Seconds: 1, Nanos: default); - } - - /// - /// Further details relating to a quota violation. - /// - /// The subject where the quota violation occured e.g and ip address or remote resource. - /// Further information relating to the quota violation. - public sealed record DaprQuotaFailureViolation(string Subject, string Description); - - /// - /// Detail relating to a quota failure e.g reaching API limit. - /// - public sealed record DaprQuotaFailureDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.QuotaFailure) - { - /// - /// Collection of . - /// - public IReadOnlyCollection Violations { get; init; } = Array.Empty(); - } - - /// - /// Further infomation related to a bad request. - /// - /// The field that generated the bad request e.g 'NewAccountName||'. - /// Further description of the field error e.g 'Account name cannot contain '||'' - public sealed record DaprBadRequestDetailFieldViolation(string Field, string Description); - - /// - /// Detail containing information related to a bad request from the client. - /// - public sealed record DaprBadRequestDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.BadRequest) - { - /// - /// Collection of . - /// - public IReadOnlyCollection FieldViolations { get; init; } = Array.Empty(); - } - - /// - /// Detail containing request info used by the client to provide back to the server in relation to filing bugs, providing feedback, or general debugging by the server. - /// - /// A string understandable by the server e.g an internal UID related a trace. - /// Any data that furthers server debugging. - public sealed record DaprRequestInfoDetail(string RequestId, string ServingData) : DaprExtendedErrorDetail(DaprExtendedErrorType.RequestInfo); +/// +/// Provides the time offset the client should use before retrying. +/// +/// Second offset. +/// Nano offset. +public sealed record DaprRetryDelay(long Seconds, int Nanos); +/// +/// Detail containing retry information. Provides the minimum amount of the time the client should wait before retrying a request. +/// +public sealed record DaprRetryInfoDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.RetryInfo) +{ /// - /// Detail containing a message that can be localized. + /// A . /// - /// The locale e.g 'en-US'. - /// A message to be localized. - public sealed record DaprLocalizedMessageDetail(string Locale, string Message) : DaprExtendedErrorDetail(DaprExtendedErrorType.LocalizedMessage); + public DaprRetryDelay Delay = new(Seconds: 1, Nanos: default); +} +/// +/// Further details relating to a quota violation. +/// +/// The subject where the quota violation occured e.g and ip address or remote resource. +/// Further information relating to the quota violation. +public sealed record DaprQuotaFailureViolation(string Subject, string Description); +/// +/// Detail relating to a quota failure e.g reaching API limit. +/// +public sealed record DaprQuotaFailureDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.QuotaFailure) +{ /// - /// Contains a link to a help resource. + /// Collection of . /// - /// Url to help resources or documentation e.g 'https://v1-15.docs.dapr.io/developing-applications/error-codes/error-codes-reference/'. - /// A description of the link. - public sealed record DaprHelpDetailLink(string Url, string Description); + public IReadOnlyCollection Violations { get; init; } = Array.Empty(); +} - /// - /// Detail containing links to further help resources. - /// - public sealed record DaprHelpDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.Help) - { - /// - /// Collection of . - /// - public IReadOnlyCollection Links { get; init; } = Array.Empty(); - } +/// +/// Further infomation related to a bad request. +/// +/// The field that generated the bad request e.g 'NewAccountName||'. +/// Further description of the field error e.g 'Account name cannot contain '||'' +public sealed record DaprBadRequestDetailFieldViolation(string Field, string Description); +/// +/// Detail containing information related to a bad request from the client. +/// +public sealed record DaprBadRequestDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.BadRequest) +{ /// - /// Detail containg resource information. - /// - /// The type of the resource e.g 'state'. - /// The name of the resource e.g 'statestore'. - /// The owner of the resource. - /// Further description of the resource. - public sealed record DaprResourceInfoDetail(string ResourceType, string ResourceName, string Owner, string Description) : DaprExtendedErrorDetail(DaprExtendedErrorType.ResourceInfo); - + /// Collection of . + /// + public IReadOnlyCollection FieldViolations { get; init; } = Array.Empty(); +} + +/// +/// Detail containing request info used by the client to provide back to the server in relation to filing bugs, providing feedback, or general debugging by the server. +/// +/// A string understandable by the server e.g an internal UID related a trace. +/// Any data that furthers server debugging. +public sealed record DaprRequestInfoDetail(string RequestId, string ServingData) : DaprExtendedErrorDetail(DaprExtendedErrorType.RequestInfo); + +/// +/// Detail containing a message that can be localized. +/// +/// The locale e.g 'en-US'. +/// A message to be localized. +public sealed record DaprLocalizedMessageDetail(string Locale, string Message) : DaprExtendedErrorDetail(DaprExtendedErrorType.LocalizedMessage); + + +/// +/// Contains a link to a help resource. +/// +/// Url to help resources or documentation e.g 'https://v1-15.docs.dapr.io/developing-applications/error-codes/error-codes-reference/'. +/// A description of the link. +public sealed record DaprHelpDetailLink(string Url, string Description); + +/// +/// Detail containing links to further help resources. +/// +public sealed record DaprHelpDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.Help) +{ /// - /// Detail containing information related to a server error. - /// - /// The error reason e.g 'DAPR_STATE_ILLEGAL_KEY'. - /// The error domain e.g 'dapr.io'. - /// Further key / value based metadata. - public sealed record DaprErrorInfoDetail(string Reason, string Domain, IDictionary? Metadata) : DaprExtendedErrorDetail(DaprExtendedErrorType.ErrorInfo); - -} + /// Collection of . + /// + public IReadOnlyCollection Links { get; init; } = Array.Empty(); +} + +/// +/// Detail containg resource information. +/// +/// The type of the resource e.g 'state'. +/// The name of the resource e.g 'statestore'. +/// The owner of the resource. +/// Further description of the resource. +public sealed record DaprResourceInfoDetail(string ResourceType, string ResourceName, string Owner, string Description) : DaprExtendedErrorDetail(DaprExtendedErrorType.ResourceInfo); + +/// +/// Detail containing information related to a server error. +/// +/// The error reason e.g 'DAPR_STATE_ILLEGAL_KEY'. +/// The error domain e.g 'dapr.io'. +/// Further key / value based metadata. +public sealed record DaprErrorInfoDetail(string Reason, string Domain, IDictionary? Metadata) : DaprExtendedErrorDetail(DaprExtendedErrorType.ErrorInfo); \ No newline at end of file diff --git a/src/Dapr.Common/Exceptions/DaprExtendedErrorInfo.cs b/src/Dapr.Common/Exceptions/DaprExtendedErrorInfo.cs index bcd337a38..30586b092 100644 --- a/src/Dapr.Common/Exceptions/DaprExtendedErrorInfo.cs +++ b/src/Dapr.Common/Exceptions/DaprExtendedErrorInfo.cs @@ -11,18 +11,17 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Common.Exceptions +namespace Dapr.Common.Exceptions; + +/// +/// Dapr implementation of the richer error model. +/// +/// A status code. +/// A message. +public sealed record DaprExtendedErrorInfo(int Code, string Message) { /// - /// Dapr implementation of the richer error model. + /// A collection of details that provide more information on the error. /// - /// A status code. - /// A message. - public sealed record DaprExtendedErrorInfo(int Code, string Message) - { - /// - /// A collection of details that provide more information on the error. - /// - public DaprExtendedErrorDetail[] Details { get; init; } = Array.Empty(); - } -} + public DaprExtendedErrorDetail[] Details { get; init; } = Array.Empty(); +} \ No newline at end of file diff --git a/src/Dapr.Common/Exceptions/DaprExtendedErrorType.cs b/src/Dapr.Common/Exceptions/DaprExtendedErrorType.cs index 664f6beef..109bd3927 100644 --- a/src/Dapr.Common/Exceptions/DaprExtendedErrorType.cs +++ b/src/Dapr.Common/Exceptions/DaprExtendedErrorType.cs @@ -11,80 +11,79 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Common.Exceptions +namespace Dapr.Common.Exceptions; + +/// +/// Extended error detail types. +/// This is based on the Richer Error Model (see and +/// ) +/// and is implemented by the Dapr runtime (see ). +/// +public enum DaprExtendedErrorType { /// - /// Extended error detail types. - /// This is based on the Richer Error Model (see and - /// ) - /// and is implemented by the Dapr runtime (see ). + /// Unknown extended error type. + /// Implemented by . /// - public enum DaprExtendedErrorType - { - /// - /// Unknown extended error type. - /// Implemented by . - /// - Unknown, + Unknown, - /// - /// Retry info detail type. - /// See . - /// - RetryInfo, + /// + /// Retry info detail type. + /// See . + /// + RetryInfo, - /// - /// Debug info detail type. - /// See . - /// - DebugInfo, + /// + /// Debug info detail type. + /// See . + /// + DebugInfo, - /// - /// Quote failure detail type. - /// See . - /// - QuotaFailure, + /// + /// Quote failure detail type. + /// See . + /// + QuotaFailure, - /// - /// Precondition failure detail type. - /// See . - /// - PreconditionFailure, + /// + /// Precondition failure detail type. + /// See . + /// + PreconditionFailure, - /// - /// Request info detail type. - /// See . - /// - RequestInfo, + /// + /// Request info detail type. + /// See . + /// + RequestInfo, - /// - /// Localized message detail type. - /// See . - /// - LocalizedMessage, + /// + /// Localized message detail type. + /// See . + /// + LocalizedMessage, - /// - /// Bad request detail type. - /// See . - /// - BadRequest, + /// + /// Bad request detail type. + /// See . + /// + BadRequest, - /// - /// Error info detail type. - /// See . - /// - ErrorInfo, + /// + /// Error info detail type. + /// See . + /// + ErrorInfo, - /// - /// Help detail type. - /// See . - /// - Help, + /// + /// Help detail type. + /// See . + /// + Help, - /// - /// Resource info detail type. - /// See . - /// - ResourceInfo - } -} + /// + /// Resource info detail type. + /// See . + /// + ResourceInfo +} \ No newline at end of file diff --git a/src/Dapr.Common/Exceptions/ExtendedErrorDetailFactory.cs b/src/Dapr.Common/Exceptions/ExtendedErrorDetailFactory.cs index f81b6646d..a29c50881 100644 --- a/src/Dapr.Common/Exceptions/ExtendedErrorDetailFactory.cs +++ b/src/Dapr.Common/Exceptions/ExtendedErrorDetailFactory.cs @@ -15,108 +15,107 @@ using Google.Protobuf.WellKnownTypes; using Google.Rpc; -namespace Dapr.Common.Exceptions +namespace Dapr.Common.Exceptions; + +/// +/// factory. +/// +internal static class ExtendedErrorDetailFactory { /// - /// factory. + /// Create a new from an instance of . /// - internal static class ExtendedErrorDetailFactory + /// The serialized detail message to create the error detail from. + /// A new instance of + internal static DaprExtendedErrorDetail CreateErrorDetail(Any message) { - /// - /// Create a new from an instance of . - /// - /// The serialized detail message to create the error detail from. - /// A new instance of - internal static DaprExtendedErrorDetail CreateErrorDetail(Any message) + var data = message.Value; + return message.TypeUrl switch { - var data = message.Value; - return message.TypeUrl switch - { - DaprExtendedErrorConstants.RetryInfo => ToDaprRetryInfoDetail(data), - DaprExtendedErrorConstants.ErrorInfo => ToDaprErrorInfoDetail(data), - DaprExtendedErrorConstants.DebugInfo => ToDaprDebugInfoDetail(data), - DaprExtendedErrorConstants.QuotaFailure => ToDaprQuotaFailureDetail(data), - DaprExtendedErrorConstants.PreconditionFailure => ToDaprPreconditionFailureDetail(data), - DaprExtendedErrorConstants.BadRequest => ToDaprBadRequestDetail(data), - DaprExtendedErrorConstants.RequestInfo => ToDaprRequestInfoDetail(data), - DaprExtendedErrorConstants.ResourceInfo => ToDaprResourceInfoDetail(data), - DaprExtendedErrorConstants.Help => ToDaprHelpDetail(data), - DaprExtendedErrorConstants.LocalizedMessage => ToDaprLocalizedMessageDetail(data), - _ => new DaprUnknownDetail(message.TypeUrl) - }; - } + DaprExtendedErrorConstants.RetryInfo => ToDaprRetryInfoDetail(data), + DaprExtendedErrorConstants.ErrorInfo => ToDaprErrorInfoDetail(data), + DaprExtendedErrorConstants.DebugInfo => ToDaprDebugInfoDetail(data), + DaprExtendedErrorConstants.QuotaFailure => ToDaprQuotaFailureDetail(data), + DaprExtendedErrorConstants.PreconditionFailure => ToDaprPreconditionFailureDetail(data), + DaprExtendedErrorConstants.BadRequest => ToDaprBadRequestDetail(data), + DaprExtendedErrorConstants.RequestInfo => ToDaprRequestInfoDetail(data), + DaprExtendedErrorConstants.ResourceInfo => ToDaprResourceInfoDetail(data), + DaprExtendedErrorConstants.Help => ToDaprHelpDetail(data), + DaprExtendedErrorConstants.LocalizedMessage => ToDaprLocalizedMessageDetail(data), + _ => new DaprUnknownDetail(message.TypeUrl) + }; + } - private static DaprRetryInfoDetail ToDaprRetryInfoDetail(ByteString data) - { - var retryInfo = RetryInfo.Parser.ParseFrom(data); - return new() { Delay = new DaprRetryDelay(Seconds: retryInfo.RetryDelay.Seconds, Nanos: retryInfo.RetryDelay.Nanos) } ; - } + private static DaprRetryInfoDetail ToDaprRetryInfoDetail(ByteString data) + { + var retryInfo = RetryInfo.Parser.ParseFrom(data); + return new() { Delay = new DaprRetryDelay(Seconds: retryInfo.RetryDelay.Seconds, Nanos: retryInfo.RetryDelay.Nanos) } ; + } - private static DaprLocalizedMessageDetail ToDaprLocalizedMessageDetail(ByteString data) - { - var localizedMessage = LocalizedMessage.Parser.ParseFrom(data); - return new(Locale: localizedMessage.Locale, Message: localizedMessage.Message); - } + private static DaprLocalizedMessageDetail ToDaprLocalizedMessageDetail(ByteString data) + { + var localizedMessage = LocalizedMessage.Parser.ParseFrom(data); + return new(Locale: localizedMessage.Locale, Message: localizedMessage.Message); + } - private static DaprDebugInfoDetail ToDaprDebugInfoDetail(ByteString data) - { - var debugInfo = DebugInfo.Parser.ParseFrom(data); - return new(StackEntries: debugInfo.StackEntries.ToArray(), Detail: debugInfo.Detail); - } + private static DaprDebugInfoDetail ToDaprDebugInfoDetail(ByteString data) + { + var debugInfo = DebugInfo.Parser.ParseFrom(data); + return new(StackEntries: debugInfo.StackEntries.ToArray(), Detail: debugInfo.Detail); + } - private static DaprQuotaFailureDetail ToDaprQuotaFailureDetail(ByteString data) + private static DaprQuotaFailureDetail ToDaprQuotaFailureDetail(ByteString data) + { + var quotaFailure = QuotaFailure.Parser.ParseFrom(data); + return new() { - var quotaFailure = QuotaFailure.Parser.ParseFrom(data); - return new() - { - Violations = quotaFailure.Violations.Select(violation => new DaprQuotaFailureViolation(Subject: violation.Subject, Description: violation.Description)).ToArray(), - }; - } + Violations = quotaFailure.Violations.Select(violation => new DaprQuotaFailureViolation(Subject: violation.Subject, Description: violation.Description)).ToArray(), + }; + } - private static DaprPreconditionFailureDetail ToDaprPreconditionFailureDetail(ByteString data) + private static DaprPreconditionFailureDetail ToDaprPreconditionFailureDetail(ByteString data) + { + var preconditionFailure = PreconditionFailure.Parser.ParseFrom(data); + return new() { - var preconditionFailure = PreconditionFailure.Parser.ParseFrom(data); - return new() - { - Violations = preconditionFailure.Violations.Select(violation => new DaprPreconditionFailureViolation(Type: violation.Type, Subject: violation.Subject, Description: violation.Description)).ToArray() - }; - } + Violations = preconditionFailure.Violations.Select(violation => new DaprPreconditionFailureViolation(Type: violation.Type, Subject: violation.Subject, Description: violation.Description)).ToArray() + }; + } - private static DaprRequestInfoDetail ToDaprRequestInfoDetail(ByteString data) - { - var requestInfo = RequestInfo.Parser.ParseFrom(data); - return new(RequestId: requestInfo.RequestId, ServingData: requestInfo.ServingData); - } + private static DaprRequestInfoDetail ToDaprRequestInfoDetail(ByteString data) + { + var requestInfo = RequestInfo.Parser.ParseFrom(data); + return new(RequestId: requestInfo.RequestId, ServingData: requestInfo.ServingData); + } - private static DaprResourceInfoDetail ToDaprResourceInfoDetail(ByteString data) - { - var resourceInfo = ResourceInfo.Parser.ParseFrom(data); - return new(ResourceType: resourceInfo.ResourceType, ResourceName: resourceInfo.ResourceName, Owner: resourceInfo.Owner, Description: resourceInfo.Description); - } + private static DaprResourceInfoDetail ToDaprResourceInfoDetail(ByteString data) + { + var resourceInfo = ResourceInfo.Parser.ParseFrom(data); + return new(ResourceType: resourceInfo.ResourceType, ResourceName: resourceInfo.ResourceName, Owner: resourceInfo.Owner, Description: resourceInfo.Description); + } - private static DaprBadRequestDetail ToDaprBadRequestDetail(ByteString data) + private static DaprBadRequestDetail ToDaprBadRequestDetail(ByteString data) + { + var badRequest = BadRequest.Parser.ParseFrom(data); + return new() { - var badRequest = BadRequest.Parser.ParseFrom(data); - return new() - { - FieldViolations = badRequest.FieldViolations.Select( - fieldViolation => new DaprBadRequestDetailFieldViolation(Field: fieldViolation.Field, Description: fieldViolation.Description)).ToArray() - }; - } + FieldViolations = badRequest.FieldViolations.Select( + fieldViolation => new DaprBadRequestDetailFieldViolation(Field: fieldViolation.Field, Description: fieldViolation.Description)).ToArray() + }; + } - private static DaprErrorInfoDetail ToDaprErrorInfoDetail(ByteString data) - { - var errorInfo = ErrorInfo.Parser.ParseFrom(data); - return new(Reason: errorInfo.Reason, Domain: errorInfo.Domain, Metadata: errorInfo.Metadata); - } + private static DaprErrorInfoDetail ToDaprErrorInfoDetail(ByteString data) + { + var errorInfo = ErrorInfo.Parser.ParseFrom(data); + return new(Reason: errorInfo.Reason, Domain: errorInfo.Domain, Metadata: errorInfo.Metadata); + } - private static DaprHelpDetail ToDaprHelpDetail(ByteString data) + private static DaprHelpDetail ToDaprHelpDetail(ByteString data) + { + var helpInfo = Help.Parser.ParseFrom(data); + return new() { - var helpInfo = Help.Parser.ParseFrom(data); - return new() - { - Links = helpInfo.Links.Select(link => new DaprHelpDetailLink(Url: link.Url, Description: link.Description)).ToArray() - }; - } + Links = helpInfo.Links.Select(link => new DaprHelpDetailLink(Url: link.Url, Description: link.Description)).ToArray() + }; } -} +} \ No newline at end of file From afa20687e47aff0e7067b984eeeefc701dac89a2 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 06:13:26 -0500 Subject: [PATCH 04/12] Using file-scoped namespaces throughout solution Signed-off-by: Whit Waldo --- examples/Actor/ActorClient/Program.cs | 235 +- examples/Actor/DemoActor/BankService.cs | 29 +- examples/Actor/DemoActor/DemoActor.cs | 321 +- examples/Actor/DemoActor/Program.cs | 27 +- examples/Actor/DemoActor/Startup.cs | 61 +- examples/Actor/IDemoActor/IBankActor.cs | 41 +- examples/Actor/IDemoActor/IDemoActor.cs | 233 +- .../AspNetCore/ControllerSample/Account.cs | 25 +- .../Controllers/SampleController.cs | 475 ++- .../ControllerSample/CustomTopicAttribute.cs | 47 +- .../AspNetCore/ControllerSample/Program.cs | 53 +- .../AspNetCore/ControllerSample/Startup.cs | 103 +- .../ControllerSample/Transaction.cs | 31 +- .../ControllerSample/TransactionV2.cs | 43 +- .../GrpcServiceSample/Models/Account.cs | 27 +- .../Models/GetAccountInput.cs | 19 +- .../GrpcServiceSample/Models/Transaction.cs | 31 +- .../AspNetCore/GrpcServiceSample/Program.cs | 57 +- .../Services/BankingService.cs | 263 +- .../AspNetCore/GrpcServiceSample/Startup.cs | 63 +- examples/AspNetCore/RoutingSample/Account.cs | 25 +- examples/AspNetCore/RoutingSample/Program.cs | 53 +- examples/AspNetCore/RoutingSample/Startup.cs | 393 ++- .../AspNetCore/RoutingSample/Transaction.cs | 25 +- .../Program.cs | 103 +- .../Startup.cs | 105 +- .../Controllers/ConfigurationController.cs | 119 +- examples/Client/ConfigurationApi/Program.cs | 57 +- examples/Client/ConfigurationApi/Startup.cs | 77 +- examples/Client/Cryptography/Example.cs | 13 +- .../EncryptDecryptFileStreamExample.cs | 85 +- .../Examples/EncryptDecryptStringExample.cs | 37 +- examples/Client/Cryptography/Program.cs | 51 +- .../Controllers/BindingController.cs | 141 +- .../Client/DistributedLock/Model/StateData.cs | 22 +- examples/Client/DistributedLock/Program.cs | 31 +- .../Services/GeneratorService.cs | 35 +- examples/Client/DistributedLock/Startup.cs | 57 +- .../BulkPublishEventExample.cs | 61 +- .../BulkPublishEventExample/Example.cs | 13 +- .../BulkPublishEventExample/Program.cs | 43 +- .../PublishEventExample/Example.cs | 15 +- .../PublishEventExample/Program.cs | 45 +- .../PublishBytesExample.cs | 25 +- .../PublishEventExample.cs | 23 +- examples/Client/ServiceInvocation/Example.cs | 13 +- .../InvokeServiceGrpcExample.cs | 45 +- .../InvokeServiceHttpClientExample.cs | 55 +- .../InvokeServiceHttpExample.cs | 79 +- examples/Client/ServiceInvocation/Program.cs | 47 +- .../StateManagement/BulkStateExample.cs | 65 +- examples/Client/StateManagement/Example.cs | 13 +- examples/Client/StateManagement/Program.cs | 51 +- .../StateStoreBinaryExample.cs | 57 +- .../StateManagement/StateStoreETagsExample.cs | 89 +- .../StateManagement/StateStoreExample.cs | 57 +- .../StateStoreTransactionsExample.cs | 65 +- .../ActorClient/IGenericClientActor.cs | 19 +- .../Activities/NotifyActivity.cs | 29 +- .../Activities/ProcessPaymentActivity.cs | 47 +- .../Activities/RequestApprovalActivity.cs | 29 +- .../Activities/ReserveInventoryActivity.cs | 87 +- .../Activities/UpdateInventoryActivity.cs | 87 +- .../Workflow/WorkflowConsoleApp/Models.cs | 27 +- .../Workflows/OrderProcessingWorkflow.cs | 183 +- .../WorkflowUnitTest/OrderProcessingTests.cs | 123 +- .../ActorsEndpointRouteBuilderExtensions.cs | 291 +- .../ActorsServiceCollectionExtensions.cs | 147 +- .../DependencyInjectionActorActivator.cs | 127 +- ...ependencyInjectionActorActivatorFactory.cs | 27 +- src/Dapr.Actors.Generators/Constants.cs | 59 +- ...CancellationTokensMustBeTheLastArgument.cs | 39 +- ...tOptionallyFollowedByACancellationToken.cs | 39 +- .../DiagnosticsException.cs | 35 +- .../Extensions/IEnumerableExtensions.cs | 47 +- .../Helpers/SyntaxFactoryHelpers.cs | 267 +- .../Models/ActorClientDescriptor.cs | 79 +- src/Dapr.Actors.Generators/Templates.cs | 79 +- src/Dapr.Actors/ActorId.cs | 263 +- .../ActorMethodInvocationException.cs | 67 +- src/Dapr.Actors/ActorReentrancyConfig.cs | 65 +- .../ActorReentrancyContextAccessor.cs | 57 +- src/Dapr.Actors/ActorReference.cs | 137 +- src/Dapr.Actors/Builder/ActorCodeBuilder.cs | 255 +- .../Builder/ActorCodeBuilderNames.cs | 197 +- .../Builder/ActorMethodDispatcherBase.cs | 413 ++- .../Builder/ActorProxyGenerator.cs | 37 +- .../Builder/ActorProxyGeneratorBuildResult.cs | 25 +- .../Builder/ActorProxyGeneratorBuilder.cs | 931 +++--- src/Dapr.Actors/Builder/BuildResult.cs | 17 +- .../Builder/CodeBuilderAttribute.cs | 71 +- src/Dapr.Actors/Builder/CodeBuilderContext.cs | 39 +- src/Dapr.Actors/Builder/CodeBuilderModule.cs | 39 +- src/Dapr.Actors/Builder/CodeBuilderUtils.cs | 381 ++- src/Dapr.Actors/Builder/ICodeBuilder.cs | 59 +- src/Dapr.Actors/Builder/ICodeBuilderNames.cs | 223 +- src/Dapr.Actors/Builder/IProxyActivator.cs | 25 +- src/Dapr.Actors/Builder/InterfaceDetails.cs | 47 +- .../Builder/InterfaceDetailsStore.cs | 131 +- src/Dapr.Actors/Builder/MethodBodyTypes.cs | 19 +- .../Builder/MethodBodyTypesBuildResult.cs | 63 +- .../Builder/MethodBodyTypesBuilder.cs | 171 +- .../Builder/MethodDispatcherBuildResult.cs | 23 +- .../Builder/MethodDispatcherBuilder.cs | 723 +++-- src/Dapr.Actors/Client/ActorProxy.cs | 545 ++-- src/Dapr.Actors/Client/ActorProxyFactory.cs | 137 +- src/Dapr.Actors/Client/ActorProxyOptions.cs | 95 +- src/Dapr.Actors/Client/IActorProxy.cs | 31 +- src/Dapr.Actors/Client/IActorProxyFactory.cs | 79 +- src/Dapr.Actors/ClientSettings.cs | 37 +- src/Dapr.Actors/Common/CRC64.cs | 227 +- src/Dapr.Actors/Common/IdUtil.cs | 127 +- .../ActorDataContractSurrogate.cs | 75 +- .../Communication/ActorInvokeException.cs | 137 +- .../Communication/ActorInvokeExceptionData.cs | 67 +- .../Communication/ActorLogicalCallContext.cs | 45 +- ...geBodyDataContractSerializationProvider.cs | 421 ++- .../ActorMessageBodyJsonConverter.cs | 115 +- ...torMessageBodyJsonSerializationProvider.cs | 259 +- .../ActorMessageHeaderSerializer.cs | 147 +- .../ActorMessageSerializersManager.cs | 163 +- .../Communication/ActorMethodDispatcherMap.cs | 59 +- .../Communication/ActorRequestMessage.cs | 37 +- .../Communication/ActorRequestMessageBody.cs | 43 +- .../ActorRequestMessageHeader.cs | 137 +- .../Communication/ActorResponseMessage.cs | 41 +- .../Communication/ActorResponseMessageBody.cs | 33 +- .../ActorResponseMessageHeader.cs | 89 +- .../Communication/ActorStateResponse.cs | 61 +- src/Dapr.Actors/Communication/CacheEntry.cs | 25 +- .../Client/ActorNonRemotingClient.cs | 49 +- .../Client/ActorRemotingClient.cs | 77 +- .../DataContractMessageFactory.cs | 21 +- .../Communication/DisposableStream.cs | 125 +- .../Communication/IActorMessageBodyFactory.cs | 45 +- .../IActorMessageBodySerializationProvider.cs | 83 +- .../IActorMessageHeaderSerializer.cs | 63 +- .../Communication/IActorRequestMessage.cs | 29 +- .../Communication/IActorRequestMessageBody.cs | 47 +- .../IActorRequestMessageBodySerializer.cs | 41 +- .../IActorRequestMessageHeader.cs | 97 +- .../Communication/IActorResponseMessage.cs | 31 +- .../IActorResponseMessageBody.cs | 39 +- .../IActorResponseMessageBodySerializer.cs | 41 +- .../IActorResponseMessageHeader.cs | 49 +- src/Dapr.Actors/Constants.cs | 109 +- src/Dapr.Actors/DaprHttpInteractor.cs | 759 +++-- .../Description/ActorInterfaceDescription.cs | 97 +- .../Description/InterfaceDescription.cs | 349 ++- .../Description/MethodArgumentDescription.cs | 145 +- .../Description/MethodDescription.cs | 151 +- .../Description/MethodReturnCheck.cs | 13 +- src/Dapr.Actors/Description/TypeUtility.cs | 31 +- src/Dapr.Actors/Helper.cs | 39 +- src/Dapr.Actors/IActor.cs | 15 +- src/Dapr.Actors/IActorReference.cs | 33 +- src/Dapr.Actors/IDaprInteractor.cs | 187 +- src/Dapr.Actors/JsonSerializerDefaults.cs | 23 +- src/Dapr.Actors/Runtime/Actor.cs | 1033 ++++--- src/Dapr.Actors/Runtime/ActorActivator.cs | 65 +- .../Runtime/ActorActivatorFactory.cs | 25 +- .../Runtime/ActorActivatorState.cs | 37 +- src/Dapr.Actors/Runtime/ActorAttribute.cs | 29 +- src/Dapr.Actors/Runtime/ActorCallType.cs | 43 +- src/Dapr.Actors/Runtime/ActorHost.cs | 277 +- src/Dapr.Actors/Runtime/ActorManager.cs | 613 ++-- src/Dapr.Actors/Runtime/ActorMethodContext.cs | 87 +- src/Dapr.Actors/Runtime/ActorMethodInfoMap.cs | 51 +- src/Dapr.Actors/Runtime/ActorRegistration.cs | 71 +- .../Runtime/ActorRegistrationCollection.cs | 123 +- src/Dapr.Actors/Runtime/ActorReminder.cs | 371 ++- .../Runtime/ActorReminderOptions.cs | 75 +- src/Dapr.Actors/Runtime/ActorReminderToken.cs | 85 +- src/Dapr.Actors/Runtime/ActorRuntime.cs | 271 +- .../Runtime/ActorRuntimeOptions.cs | 339 ++- src/Dapr.Actors/Runtime/ActorStateChange.cs | 123 +- src/Dapr.Actors/Runtime/ActorStateManager.cs | 771 +++-- src/Dapr.Actors/Runtime/ActorTestOptions.cs | 159 +- src/Dapr.Actors/Runtime/ActorTimer.cs | 289 +- src/Dapr.Actors/Runtime/ActorTimerManager.cs | 71 +- src/Dapr.Actors/Runtime/ActorTimerOptions.cs | 91 +- src/Dapr.Actors/Runtime/ActorTimerToken.cs | 85 +- .../Runtime/ActorTypeExtensions.cs | 139 +- .../Runtime/ActorTypeInformation.cs | 267 +- src/Dapr.Actors/Runtime/ConditionalValue.cs | 51 +- src/Dapr.Actors/Runtime/DaprStateProvider.cs | 273 +- .../Runtime/DefaultActorActivator.cs | 85 +- .../Runtime/DefaultActorActivatorFactory.cs | 25 +- .../Runtime/DefaultActorTimerManager.cs | 147 +- .../Runtime/IActorContextualState.cs | 15 +- src/Dapr.Actors/Runtime/IActorReminder.cs | 67 +- src/Dapr.Actors/Runtime/IActorStateManager.cs | 629 ++-- .../Runtime/IActorStateSerializer.cs | 39 +- src/Dapr.Actors/Runtime/IRemindable.cs | 43 +- src/Dapr.Actors/Runtime/StateChangeKind.cs | 43 +- src/Dapr.Actors/Runtime/TimerInfo.cs | 251 +- .../Serialization/ActorIdJsonConverter.cs | 73 +- src/Dapr.AspNetCore/BulkMessageModel.cs | 105 +- .../BulkSubscribeAppResponse.cs | 33 +- .../BulkSubscribeAppResponseEntry.cs | 45 +- .../BulkSubscribeAppResponseStatus.cs | 40 +- src/Dapr.AspNetCore/BulkSubscribeAttribute.cs | 99 +- src/Dapr.AspNetCore/BulkSubscribeMessage.cs | 71 +- .../BulkSubscribeMessageEntry.cs | 85 +- .../BulkSubscribeTopicOptions.cs | 37 +- .../CloudEventPropertyNames.cs | 15 +- src/Dapr.AspNetCore/CloudEventsMiddleware.cs | 457 ++- .../CloudEventsMiddlewareOptions.cs | 115 +- .../DaprApplicationBuilderExtensions.cs | 69 +- .../DaprAuthenticationBuilderExtensions.cs | 59 +- .../DaprAuthenticationHandler.cs | 91 +- .../DaprAuthenticationOptions.cs | 29 +- .../DaprAuthorizationOptionsExtensions.cs | 35 +- ...DaprEndpointConventionBuilderExtensions.cs | 255 +- .../DaprEndpointRouteBuilderExtensions.cs | 321 +- .../DaprMvcBuilderExtensions.cs | 65 +- src/Dapr.AspNetCore/FromStateAttribute.cs | 89 +- src/Dapr.AspNetCore/FromStateBindingSource.cs | 25 +- src/Dapr.AspNetCore/IBulkSubscribeMetadata.cs | 37 +- .../IDeadLetterTopicMetadata.cs | 20 +- src/Dapr.AspNetCore/IOriginalTopicMetadata.cs | 43 +- .../IOwnedOriginalTopicMetadata.cs | 31 +- src/Dapr.AspNetCore/IRawTopicMetadata.cs | 19 +- src/Dapr.AspNetCore/ITopicMetadata.cs | 43 +- .../StateEntryApplicationModelProvider.cs | 79 +- src/Dapr.AspNetCore/StateEntryModelBinder.cs | 161 +- .../StateEntryModelBinderProvider.cs | 73 +- src/Dapr.AspNetCore/SubscribeOptions.cs | 19 +- src/Dapr.AspNetCore/Subscription.cs | 187 +- src/Dapr.AspNetCore/TopicAttribute.cs | 269 +- src/Dapr.AspNetCore/TopicMetadataAttribute.cs | 81 +- src/Dapr.AspNetCore/TopicOptions.cs | 85 +- src/Dapr.Client/BindingRequest.cs | 83 +- src/Dapr.Client/BindingResponse.cs | 63 +- src/Dapr.Client/BulkPublishEntry.cs | 73 +- src/Dapr.Client/BulkPublishResponse.cs | 33 +- .../BulkPublishResponseFailedEntry.cs | 45 +- src/Dapr.Client/BulkStateItem.cs | 125 +- src/Dapr.Client/CloudEvent.cs | 89 +- src/Dapr.Client/ConcurrencyMode.cs | 27 +- src/Dapr.Client/ConfigurationItem.cs | 59 +- src/Dapr.Client/ConfigurationSource.cs | 23 +- src/Dapr.Client/ConsistencyMode.cs | 27 +- src/Dapr.Client/Constants.cs | 15 +- src/Dapr.Client/CryptographyEnums.cs | 109 +- src/Dapr.Client/CryptographyOptions.cs | 111 +- src/Dapr.Client/DaprApiException.cs | 197 +- src/Dapr.Client/DaprClient.cs | 2697 ++++++++--------- src/Dapr.Client/DaprClientBuilder.cs | 309 +- src/Dapr.Client/DaprError.cs | 31 +- src/Dapr.Client/DaprMetadata.cs | 177 +- .../DaprSubscribeConfigurationSource.cs | 119 +- src/Dapr.Client/DeleteBulkStateItem.cs | 69 +- src/Dapr.Client/Extensions/EnumExtensions.cs | 35 +- src/Dapr.Client/Extensions/HttpExtensions.cs | 51 +- src/Dapr.Client/GetConfigurationResponse.cs | 43 +- src/Dapr.Client/InvocationException.cs | 79 +- src/Dapr.Client/InvocationHandler.cs | 219 +- src/Dapr.Client/InvocationInterceptor.cs | 203 +- src/Dapr.Client/SaveStateItem.cs | 81 +- src/Dapr.Client/StartWorkflowResponse.cs | 15 +- src/Dapr.Client/StateEntry.cs | 233 +- src/Dapr.Client/StateOperationType.cs | 27 +- src/Dapr.Client/StateOptions.cs | 27 +- src/Dapr.Client/StateQueryException.cs | 51 +- src/Dapr.Client/StateQueryResponse.cs | 123 +- src/Dapr.Client/StateTransactionRequest.cs | 97 +- .../SubscribeConfigurationResponse.cs | 55 +- src/Dapr.Client/TryLockResponse.cs | 73 +- src/Dapr.Client/TypeConverters.cs | 79 +- src/Dapr.Client/UnlockResponse.cs | 69 +- .../UnsubscribeConfigurationResponse.cs | 45 +- .../DaprConfigurationStoreExtension.cs | 139 +- .../DaprConfigurationStoreProvider.cs | 161 +- .../DaprConfigurationStoreSource.cs | 77 +- .../DaprSecretDescriptor.cs | 93 +- .../DaprSecretStoreConfigurationExtensions.cs | 349 ++- .../DaprSecretStoreConfigurationProvider.cs | 393 ++- .../DaprSecretStoreConfigurationSource.cs | 93 +- src/Dapr.Jobs/DaprJobsClientBuilder.cs | 11 +- .../DaprPublishSubscribeClientBuilder.cs | 11 +- .../DaprWorkflowActivityContext.cs | 37 +- src/Dapr.Workflow/DaprWorkflowClient.cs | 537 ++-- src/Dapr.Workflow/DaprWorkflowContext.cs | 203 +- src/Dapr.Workflow/Workflow.cs | 225 +- src/Dapr.Workflow/WorkflowActivity.cs | 137 +- src/Dapr.Workflow/WorkflowActivityContext.cs | 31 +- src/Dapr.Workflow/WorkflowContext.cs | 601 ++-- src/Dapr.Workflow/WorkflowLoggingService.cs | 93 +- src/Dapr.Workflow/WorkflowRetryPolicy.cs | 155 +- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 274 +- src/Dapr.Workflow/WorkflowRuntimeStatus.cs | 79 +- src/Dapr.Workflow/WorkflowState.cs | 249 +- .../WorkflowTaskFailedException.cs | 41 +- .../WorkflowTaskFailureDetails.cs | 99 +- src/Dapr.Workflow/WorkflowTaskOptions.cs | 77 +- .../DependencyInjectionActor.cs | 39 +- .../Program.cs | 51 +- .../Startup.cs | 65 +- .../ActivationTests.cs | 97 +- .../AppWebApplicationFactory.cs | 43 +- .../HostingTests.cs | 321 +- .../ActorHostingTest.cs | 137 +- ...torsEndpointRouteBuilderExtensionsTests.cs | 121 +- .../DependencyInjectionActorActivatorTests.cs | 241 +- .../Extensions/IEnumerableExtensionsTests.cs | 93 +- .../Helpers/SyntaxFactoryHelpersTests.cs | 221 +- .../Dapr.Actors.Test/ActorStateManagerTest.cs | 351 ++- test/Dapr.Actors.Test/ActorUnitTestTests.cs | 299 +- test/Dapr.Actors.Test/ApiTokenTests.cs | 145 +- .../DaprFormatTimeSpanTests.cs | 241 +- .../DaprHttpInteractorTest.cs | 621 ++-- .../Dapr.Actors.Test/DaprStateProviderTest.cs | 219 +- .../ActorInterfaceDescriptionTests.cs | 271 +- .../Description/InterfaceDescriptionTests.cs | 371 ++- .../MethodArgumentDescriptionTests.cs | 199 +- .../Description/MethodDescriptionTests.cs | 215 +- .../Runtime/ActorManagerTests.cs | 347 ++- .../Runtime/ActorReminderInfoTests.cs | 43 +- .../Runtime/ActorRuntimeOptionsTests.cs | 215 +- .../Runtime/ActorRuntimeTests.cs | 697 +++-- .../Runtime/ActorTypeInformationTests.cs | 99 +- .../Runtime/DefaultActorActivatorTests.cs | 139 +- .../CustomTopicAttribute.cs | 29 +- .../DaprController.cs | 319 +- .../Program.cs | 33 +- .../Startup.cs | 97 +- .../UserInfo.cs | 15 +- .../Widget.cs | 11 +- .../AppWebApplicationFactory.cs | 43 +- .../AuthenticationTest.cs | 75 +- .../CloudEventsIntegrationTest.cs | 185 +- .../ControllerIntegrationTest.cs | 237 +- .../RoutingIntegrationTest.cs | 43 +- .../StateTestClient.cs | 169 +- .../SubscribeEndpointTest.cs | 211 +- .../CloudEventsMiddlewareTest.cs | 795 +++-- .../DaprClientBuilderTest.cs | 174 +- .../DaprMvcBuilderExtensionsTest.cs | 119 +- .../StateEntryApplicationModelProviderTest.cs | 121 +- .../StateEntryModelBinderTest.cs | 275 +- test/Dapr.Client.Test/ArgumentVerifierTest.cs | 59 +- .../BulkPublishEventApiTest.cs | 503 ++- test/Dapr.Client.Test/ConfigurationApiTest.cs | 235 +- .../ConfigurationSourceTest.cs | 157 +- test/Dapr.Client.Test/CryptographyApiTest.cs | 155 +- test/Dapr.Client.Test/DaprApiTokenTest.cs | 77 +- ...lientTest.CreateInvokableHttpClientTest.cs | 55 +- .../DaprClientTest.InvokeMethodAsync.cs | 1405 +++++---- .../DaprClientTest.InvokeMethodGrpcAsync.cs | 731 +++-- test/Dapr.Client.Test/DaprClientTest.cs | 135 +- .../DistributedLockApiTest.cs | 125 +- .../Extensions/EnumExtensionTest.cs | 53 +- .../Extensions/HttpExtensionTest.cs | 97 +- .../InvocationHandlerTests.cs | 355 ++- test/Dapr.Client.Test/InvokeBindingApiTest.cs | 497 ++- test/Dapr.Client.Test/MockClient.cs | 221 +- test/Dapr.Client.Test/PublishEventApiTest.cs | 469 ++- test/Dapr.Client.Test/SecretApiTest.cs | 605 ++-- test/Dapr.Client.Test/StateApiTest.cs | 2402 ++++++++------- test/Dapr.Client.Test/TryLockResponseTest.cs | 81 +- test/Dapr.Client.Test/TypeConvertersTest.cs | 47 +- .../DaprExtendedErrorInfoTest.cs | 1009 +++--- .../ExceptionTesting/IExceptionActor.cs | 9 +- test/Dapr.E2E.Test.Actors/IPingActor.cs | 11 +- .../ISerializationActor.cs | 25 +- .../Reentrancy/CallRecord.cs | 15 +- .../Reentrancy/IReentrantActor.cs | 11 +- .../Reentrancy/ReentrantCallOptions.cs | 11 +- test/Dapr.E2E.Test.Actors/Reentrancy/State.cs | 11 +- .../RegressionActor/IRegression762Actor.cs | 15 +- .../RegressionActor/StateCall.cs | 15 +- .../Reminders/IReminderActor.cs | 21 +- .../Reminders/StartReminderOptions.cs | 11 +- test/Dapr.E2E.Test.Actors/Reminders/State.cs | 15 +- .../Dapr.E2E.Test.Actors/State/IStateActor.cs | 13 +- .../Timers/ITimerActor.cs | 39 +- .../Timers/StartTimerOptions.cs | 11 +- test/Dapr.E2E.Test.Actors/Timers/State.cs | 15 +- .../WeaklyTypedTesting/DerivedResponse.cs | 11 +- .../IWeaklyTypedTestingActor.cs | 13 +- .../WeaklyTypedTesting/ResponseBase.cs | 14 +- .../MessageRepository.cs | 41 +- test/Dapr.E2E.Test.App.Grpc/Program.cs | 59 +- .../Services/MessagerService.cs | 53 +- test/Dapr.E2E.Test.App.Grpc/Startup.cs | 43 +- .../Actors/ReentrantActor.cs | 85 +- .../Program.cs | 27 +- .../Startup.cs | 81 +- test/Dapr.E2E.Test.App/Account.cs | 25 +- .../Actors/ExceptionActor.cs | 27 +- .../Actors/Regression762Actor.cs | 69 +- .../Dapr.E2E.Test.App/Actors/ReminderActor.cs | 143 +- .../Actors/SerializationActor.cs | 37 +- test/Dapr.E2E.Test.App/Actors/StateActor.cs | 41 +- test/Dapr.E2E.Test.App/Actors/TimerActor.cs | 83 +- .../Actors/WeaklyTypedTestingActor.cs | 45 +- .../Controllers/TestController.cs | 103 +- test/Dapr.E2E.Test.App/Program.cs | 35 +- test/Dapr.E2E.Test.App/Startup.cs | 259 +- test/Dapr.E2E.Test.App/Transaction.cs | 33 +- test/Dapr.E2E.Test/AVeryCoolWorkAroundTest.cs | 17 +- test/Dapr.E2E.Test/ActorRuntimeChecker.cs | 35 +- .../Actors/E2ETests.CustomSerializerTests.cs | 167 +- .../Actors/E2ETests.ExceptionTests.cs | 37 +- .../Actors/E2ETests.ReentrantTests.cs | 105 +- .../Actors/E2ETests.Regression762Tests.cs | 179 +- .../Actors/E2ETests.ReminderTests.cs | 259 +- .../Actors/E2ETests.StateTests.cs | 213 +- .../Actors/E2ETests.TimerTests.cs | 93 +- .../Actors/E2ETests.WeaklyTypedTests.cs | 63 +- test/Dapr.E2E.Test/DaprCommand.cs | 287 +- test/Dapr.E2E.Test/DaprRunConfiguration.cs | 21 +- test/Dapr.E2E.Test/DaprTestApp.cs | 233 +- test/Dapr.E2E.Test/DaprTestAppFixture.cs | 143 +- test/Dapr.E2E.Test/DaprTestAppLifecycle.cs | 69 +- test/Dapr.E2E.Test/E2ETests.cs | 107 +- test/Dapr.E2E.Test/HttpAssert.cs | 53 +- .../E2ETests.GrpcProxyInvocationTests.cs | 113 +- .../E2ETests.ServiceInvocationTests.cs | 131 +- .../DaprConfigurationStoreProviderTest.cs | 305 +- ...aprSecretStoreConfigurationProviderTest.cs | 1417 +++++---- .../TestHttpClient.cs | 149 +- .../MessageHandlingPolicyTest.cs | 79 +- .../WorkflowActivityTest.cs | 61 +- test/Shared/AppCallbackClient.cs | 95 +- 426 files changed, 30797 insertions(+), 31242 deletions(-) diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs index 950869b2b..af209dc1d 100644 --- a/examples/Actor/ActorClient/Program.cs +++ b/examples/Actor/ActorClient/Program.cs @@ -14,143 +14,142 @@ using Dapr.Actors.Communication; using IDemoActor; -namespace ActorClient +namespace ActorClient; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.Actors.Client; + +/// +/// Actor Client class. +/// +public class Program { - using System; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.Actors.Client; - /// - /// Actor Client class. + /// Entry point. /// - public class Program + /// Arguments. + /// A representing the asynchronous operation. + public static async Task Main(string[] args) { - /// - /// Entry point. - /// - /// Arguments. - /// A representing the asynchronous operation. - public static async Task Main(string[] args) + var data = new MyData() { - var data = new MyData() - { - PropertyA = "ValueA", - PropertyB = "ValueB", - }; + PropertyA = "ValueA", + PropertyB = "ValueB", + }; - // Create an actor Id. - var actorId = new ActorId("abc"); + // Create an actor Id. + var actorId = new ActorId("abc"); - // Make strongly typed Actor calls with Remoting. - // DemoActor is the type registered with Dapr runtime in the service. - var proxy = ActorProxy.Create(actorId, "DemoActor"); + // Make strongly typed Actor calls with Remoting. + // DemoActor is the type registered with Dapr runtime in the service. + var proxy = ActorProxy.Create(actorId, "DemoActor"); - Console.WriteLine("Making call using actor proxy to save data."); - await proxy.SaveData(data, TimeSpan.FromMinutes(10)); - Console.WriteLine("Making call using actor proxy to get data."); - var receivedData = await proxy.GetData(); - Console.WriteLine($"Received data is {receivedData}."); + Console.WriteLine("Making call using actor proxy to save data."); + await proxy.SaveData(data, TimeSpan.FromMinutes(10)); + Console.WriteLine("Making call using actor proxy to get data."); + var receivedData = await proxy.GetData(); + Console.WriteLine($"Received data is {receivedData}."); - // Making some more calls to test methods. - try + // Making some more calls to test methods. + try + { + Console.WriteLine("Making calls to an actor method which has no argument and no return type."); + await proxy.TestNoArgumentNoReturnType(); + } + catch (Exception ex) + { + Console.WriteLine($"ERROR: Got exception while making call to method with No Argument & No Return Type. Exception: {ex}"); + } + + try + { + await proxy.TestThrowException(); + } + catch (ActorMethodInvocationException ex) + { + if (ex.InnerException is ActorInvokeException invokeEx && invokeEx.ActualExceptionType is "System.NotImplementedException") { - Console.WriteLine("Making calls to an actor method which has no argument and no return type."); - await proxy.TestNoArgumentNoReturnType(); + Console.WriteLine($"Got Correct Exception from actor method invocation."); } - catch (Exception ex) + else { - Console.WriteLine($"ERROR: Got exception while making call to method with No Argument & No Return Type. Exception: {ex}"); + Console.WriteLine($"Got Incorrect Exception from actor method invocation. Exception {ex.InnerException}"); } + } + + // Making calls without Remoting, this shows method invocation using InvokeMethodAsync methods, the method name and its payload is provided as arguments to InvokeMethodAsync methods. + Console.WriteLine("Making calls without Remoting."); + var nonRemotingProxy = ActorProxy.Create(actorId, "DemoActor"); + await nonRemotingProxy.InvokeMethodAsync("TestNoArgumentNoReturnType"); + await nonRemotingProxy.InvokeMethodAsync("SaveData", data); + await nonRemotingProxy.InvokeMethodAsync("GetData"); + + Console.WriteLine("Registering the timer and reminder"); + await proxy.RegisterTimer(); + await proxy.RegisterReminder(); + Console.WriteLine("Waiting so the timer and reminder can be triggered"); + await Task.Delay(6000); + + Console.WriteLine("Making call using actor proxy to get data after timer and reminder triggered"); + receivedData = await proxy.GetData(); + Console.WriteLine($"Received data is {receivedData}."); + + Console.WriteLine("Getting details of the registered reminder"); + var reminder = await proxy.GetReminder(); + Console.WriteLine($"Received reminder is {reminder}."); + + Console.WriteLine("Deregistering timer. Timers would any way stop if the actor is deactivated as part of Dapr garbage collection."); + await proxy.UnregisterTimer(); + Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); + await proxy.UnregisterReminder(); + + Console.WriteLine("Registering reminder with repetitions - The reminder will repeat 3 times."); + await proxy.RegisterReminderWithRepetitions(3); + Console.WriteLine("Waiting so the reminder can be triggered"); + await Task.Delay(5000); + Console.WriteLine("Getting details of the registered reminder"); + reminder = await proxy.GetReminder(); + Console.WriteLine($"Received reminder is {reminder?.ToString() ?? "None"} (expecting None)."); + Console.WriteLine("Registering reminder with ttl and repetitions, i.e. reminder stops when either condition is met - The reminder will repeat 2 times."); + await proxy.RegisterReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), 2); + Console.WriteLine("Getting details of the registered reminder"); + reminder = await proxy.GetReminder(); + Console.WriteLine($"Received reminder is {reminder}."); + Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); + await proxy.UnregisterReminder(); + + Console.WriteLine("Registering reminder and Timer with TTL - The reminder will self delete after 10 seconds."); + await proxy.RegisterReminderWithTtl(TimeSpan.FromSeconds(10)); + await proxy.RegisterTimerWithTtl(TimeSpan.FromSeconds(10)); + Console.WriteLine("Getting details of the registered reminder"); + reminder = await proxy.GetReminder(); + Console.WriteLine($"Received reminder is {reminder}."); + + // Track the reminder. + var timer = new Timer(async state => Console.WriteLine($"Received data: {await proxy.GetData()}"), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + await Task.Delay(TimeSpan.FromSeconds(21)); + await timer.DisposeAsync(); + + Console.WriteLine("Creating a Bank Actor"); + var bank = ActorProxy.Create(ActorId.CreateRandom(), "DemoActor"); + while (true) + { + var balance = await bank.GetAccountBalance(); + Console.WriteLine($"Balance for account '{balance.AccountId}' is '{balance.Balance:c}'."); + Console.WriteLine($"Withdrawing '{10m:c}'..."); try { - await proxy.TestThrowException(); + await bank.Withdraw(new WithdrawRequest() { Amount = 10m, }); } catch (ActorMethodInvocationException ex) { - if (ex.InnerException is ActorInvokeException invokeEx && invokeEx.ActualExceptionType is "System.NotImplementedException") - { - Console.WriteLine($"Got Correct Exception from actor method invocation."); - } - else - { - Console.WriteLine($"Got Incorrect Exception from actor method invocation. Exception {ex.InnerException}"); - } - } - - // Making calls without Remoting, this shows method invocation using InvokeMethodAsync methods, the method name and its payload is provided as arguments to InvokeMethodAsync methods. - Console.WriteLine("Making calls without Remoting."); - var nonRemotingProxy = ActorProxy.Create(actorId, "DemoActor"); - await nonRemotingProxy.InvokeMethodAsync("TestNoArgumentNoReturnType"); - await nonRemotingProxy.InvokeMethodAsync("SaveData", data); - await nonRemotingProxy.InvokeMethodAsync("GetData"); - - Console.WriteLine("Registering the timer and reminder"); - await proxy.RegisterTimer(); - await proxy.RegisterReminder(); - Console.WriteLine("Waiting so the timer and reminder can be triggered"); - await Task.Delay(6000); - - Console.WriteLine("Making call using actor proxy to get data after timer and reminder triggered"); - receivedData = await proxy.GetData(); - Console.WriteLine($"Received data is {receivedData}."); - - Console.WriteLine("Getting details of the registered reminder"); - var reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder}."); - - Console.WriteLine("Deregistering timer. Timers would any way stop if the actor is deactivated as part of Dapr garbage collection."); - await proxy.UnregisterTimer(); - Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); - await proxy.UnregisterReminder(); - - Console.WriteLine("Registering reminder with repetitions - The reminder will repeat 3 times."); - await proxy.RegisterReminderWithRepetitions(3); - Console.WriteLine("Waiting so the reminder can be triggered"); - await Task.Delay(5000); - Console.WriteLine("Getting details of the registered reminder"); - reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder?.ToString() ?? "None"} (expecting None)."); - Console.WriteLine("Registering reminder with ttl and repetitions, i.e. reminder stops when either condition is met - The reminder will repeat 2 times."); - await proxy.RegisterReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), 2); - Console.WriteLine("Getting details of the registered reminder"); - reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder}."); - Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); - await proxy.UnregisterReminder(); - - Console.WriteLine("Registering reminder and Timer with TTL - The reminder will self delete after 10 seconds."); - await proxy.RegisterReminderWithTtl(TimeSpan.FromSeconds(10)); - await proxy.RegisterTimerWithTtl(TimeSpan.FromSeconds(10)); - Console.WriteLine("Getting details of the registered reminder"); - reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder}."); - - // Track the reminder. - var timer = new Timer(async state => Console.WriteLine($"Received data: {await proxy.GetData()}"), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); - await Task.Delay(TimeSpan.FromSeconds(21)); - await timer.DisposeAsync(); - - Console.WriteLine("Creating a Bank Actor"); - var bank = ActorProxy.Create(ActorId.CreateRandom(), "DemoActor"); - while (true) - { - var balance = await bank.GetAccountBalance(); - Console.WriteLine($"Balance for account '{balance.AccountId}' is '{balance.Balance:c}'."); - - Console.WriteLine($"Withdrawing '{10m:c}'..."); - try - { - await bank.Withdraw(new WithdrawRequest() { Amount = 10m, }); - } - catch (ActorMethodInvocationException ex) - { - Console.WriteLine("Overdraft: " + ex.Message); - break; - } + Console.WriteLine("Overdraft: " + ex.Message); + break; } } } -} +} \ No newline at end of file diff --git a/examples/Actor/DemoActor/BankService.cs b/examples/Actor/DemoActor/BankService.cs index a24eadedb..a88cd8da9 100644 --- a/examples/Actor/DemoActor/BankService.cs +++ b/examples/Actor/DemoActor/BankService.cs @@ -13,24 +13,23 @@ using IDemoActor; -namespace DemoActor +namespace DemoActor; + +public class BankService { - public class BankService + // Allow overdraft of up to 50 (of whatever currency). + private readonly decimal OverdraftThreshold = -50m; + + public decimal Withdraw(decimal balance, decimal amount) { - // Allow overdraft of up to 50 (of whatever currency). - private readonly decimal OverdraftThreshold = -50m; + // Imagine putting some complex auditing logic here in addition to the basics. - public decimal Withdraw(decimal balance, decimal amount) + var updated = balance - amount; + if (updated < OverdraftThreshold) { - // Imagine putting some complex auditing logic here in addition to the basics. - - var updated = balance - amount; - if (updated < OverdraftThreshold) - { - throw new OverdraftException(balance, amount); - } - - return updated; + throw new OverdraftException(balance, amount); } + + return updated; } -} +} \ No newline at end of file diff --git a/examples/Actor/DemoActor/DemoActor.cs b/examples/Actor/DemoActor/DemoActor.cs index b5ef53e93..38defd672 100644 --- a/examples/Actor/DemoActor/DemoActor.cs +++ b/examples/Actor/DemoActor/DemoActor.cs @@ -17,195 +17,194 @@ using Dapr.Actors.Runtime; using IDemoActor; -namespace DemoActor +namespace DemoActor; + +// The following example showcases a few features of Actors +// +// Every actor should inherit from the Actor type, and must implement one or more actor interfaces. +// In this case the actor interfaces are IDemoActor and IBankActor. +// +// For Actors to use Reminders, it must derive from IRemindable. +// If you don't intend to use Reminder feature, you can skip implementing IRemindable and reminder +// specific methods which are shown in the code below. +public class DemoActor : Actor, IDemoActor.IDemoActor, IBankActor, IRemindable { - // The following example showcases a few features of Actors - // - // Every actor should inherit from the Actor type, and must implement one or more actor interfaces. - // In this case the actor interfaces are IDemoActor and IBankActor. - // - // For Actors to use Reminders, it must derive from IRemindable. - // If you don't intend to use Reminder feature, you can skip implementing IRemindable and reminder - // specific methods which are shown in the code below. - public class DemoActor : Actor, IDemoActor.IDemoActor, IBankActor, IRemindable - { - private const string StateName = "my_data"; - - private readonly BankService bank; - - public DemoActor(ActorHost host, BankService bank) - : base(host) - { - // BankService is provided by dependency injection. - // See Program.cs - this.bank = bank; - } + private const string StateName = "my_data"; - public async Task SaveData(MyData data, TimeSpan ttl) - { - Console.WriteLine($"This is Actor id {this.Id} with data {data}."); + private readonly BankService bank; - // Set State using StateManager, state is saved after the method execution. - await this.StateManager.SetStateAsync(StateName, data, ttl); - } + public DemoActor(ActorHost host, BankService bank) + : base(host) + { + // BankService is provided by dependency injection. + // See Program.cs + this.bank = bank; + } - public Task GetData() - { - // Get state using StateManager. - return this.StateManager.GetStateAsync(StateName); - } + public async Task SaveData(MyData data, TimeSpan ttl) + { + Console.WriteLine($"This is Actor id {this.Id} with data {data}."); - public Task TestThrowException() - { - throw new NotImplementedException(); - } + // Set State using StateManager, state is saved after the method execution. + await this.StateManager.SetStateAsync(StateName, data, ttl); + } - public Task TestNoArgumentNoReturnType() - { - return Task.CompletedTask; - } + public Task GetData() + { + // Get state using StateManager. + return this.StateManager.GetStateAsync(StateName); + } - public async Task RegisterReminder() - { - await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); - } + public Task TestThrowException() + { + throw new NotImplementedException(); + } - public async Task RegisterReminderWithTtl(TimeSpan ttl) - { - await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), ttl); - } + public Task TestNoArgumentNoReturnType() + { + return Task.CompletedTask; + } + + public async Task RegisterReminder() + { + await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + } + + public async Task RegisterReminderWithTtl(TimeSpan ttl) + { + await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), ttl); + } - public async Task RegisterReminderWithRepetitions(int repetitions) - { - await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions); - } + public async Task RegisterReminderWithRepetitions(int repetitions) + { + await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions); + } - public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions) - { - await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl); - } + public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions) + { + await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl); + } - public async Task GetReminder() - { - var reminder = await this.GetReminderAsync("TestReminder"); - - return reminder is not null - ? new ActorReminderData - { - Name = reminder.Name, - Period = reminder.Period, - DueTime = reminder.DueTime - } - : null; - } + public async Task GetReminder() + { + var reminder = await this.GetReminderAsync("TestReminder"); + + return reminder is not null + ? new ActorReminderData + { + Name = reminder.Name, + Period = reminder.Period, + DueTime = reminder.DueTime + } + : null; + } - public Task UnregisterReminder() - { - return this.UnregisterReminderAsync("TestReminder"); - } + public Task UnregisterReminder() + { + return this.UnregisterReminderAsync("TestReminder"); + } - public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) - { - // This method is invoked when an actor reminder is fired. - var actorState = await this.StateManager.GetStateAsync(StateName); - actorState.PropertyB = $"Reminder triggered at '{DateTime.Now:yyyy-MM-ddTHH:mm:ss}'"; - await this.StateManager.SetStateAsync(StateName, actorState, ttl: TimeSpan.FromMinutes(5)); - } + public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) + { + // This method is invoked when an actor reminder is fired. + var actorState = await this.StateManager.GetStateAsync(StateName); + actorState.PropertyB = $"Reminder triggered at '{DateTime.Now:yyyy-MM-ddTHH:mm:ss}'"; + await this.StateManager.SetStateAsync(StateName, actorState, ttl: TimeSpan.FromMinutes(5)); + } - class TimerParams - { - public int IntParam { get; set; } - public string StringParam { get; set; } - } + class TimerParams + { + public int IntParam { get; set; } + public string StringParam { get; set; } + } - /// - public Task RegisterTimer() + /// + public Task RegisterTimer() + { + var timerParams = new TimerParams { - var timerParams = new TimerParams - { - IntParam = 100, - StringParam = "timer test", - }; + IntParam = 100, + StringParam = "timer test", + }; - var serializedTimerParams = JsonSerializer.SerializeToUtf8Bytes(timerParams); - return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); - } + var serializedTimerParams = JsonSerializer.SerializeToUtf8Bytes(timerParams); + return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); + } - public Task RegisterTimerWithTtl(TimeSpan ttl) + public Task RegisterTimerWithTtl(TimeSpan ttl) + { + var timerParams = new TimerParams { - var timerParams = new TimerParams - { - IntParam = 100, - StringParam = "timer test", - }; + IntParam = 100, + StringParam = "timer test", + }; - var serializedTimerParams = JsonSerializer.SerializeToUtf8Bytes(timerParams); - return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), ttl); - } + var serializedTimerParams = JsonSerializer.SerializeToUtf8Bytes(timerParams); + return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), ttl); + } - public Task UnregisterTimer() - { - return this.UnregisterTimerAsync("TestTimer"); - } + public Task UnregisterTimer() + { + return this.UnregisterTimerAsync("TestTimer"); + } - // This method is called whenever an actor is activated. - // An actor is activated the first time any of its methods are invoked. - protected override Task OnActivateAsync() - { - // Provides opportunity to perform some optional setup. - return Task.CompletedTask; - } + // This method is called whenever an actor is activated. + // An actor is activated the first time any of its methods are invoked. + protected override Task OnActivateAsync() + { + // Provides opportunity to perform some optional setup. + return Task.CompletedTask; + } - // This method is called whenever an actor is deactivated after a period of inactivity. - protected override Task OnDeactivateAsync() - { - // Provides Opportunity to perform optional cleanup. - return Task.CompletedTask; - } - - /// - /// This method is called when the timer is triggered based on its registration. - /// It updates the PropertyA value. - /// - /// Timer input data. - /// A task that represents the asynchronous operation. - public async Task TimerCallback(byte[] data) - { - var state = await this.StateManager.GetStateAsync(StateName); - state.PropertyA = $"Timer triggered at '{DateTime.Now:yyyyy-MM-ddTHH:mm:s}'"; - await this.StateManager.SetStateAsync(StateName, state, ttl: TimeSpan.FromMinutes(5)); - var timerParams = JsonSerializer.Deserialize(data); - Console.WriteLine("Timer parameter1: " + timerParams.IntParam); - Console.WriteLine("Timer parameter2: " + timerParams.StringParam); - } - - public async Task GetAccountBalance() + // This method is called whenever an actor is deactivated after a period of inactivity. + protected override Task OnDeactivateAsync() + { + // Provides Opportunity to perform optional cleanup. + return Task.CompletedTask; + } + + /// + /// This method is called when the timer is triggered based on its registration. + /// It updates the PropertyA value. + /// + /// Timer input data. + /// A task that represents the asynchronous operation. + public async Task TimerCallback(byte[] data) + { + var state = await this.StateManager.GetStateAsync(StateName); + state.PropertyA = $"Timer triggered at '{DateTime.Now:yyyyy-MM-ddTHH:mm:s}'"; + await this.StateManager.SetStateAsync(StateName, state, ttl: TimeSpan.FromMinutes(5)); + var timerParams = JsonSerializer.Deserialize(data); + Console.WriteLine("Timer parameter1: " + timerParams.IntParam); + Console.WriteLine("Timer parameter2: " + timerParams.StringParam); + } + + public async Task GetAccountBalance() + { + var starting = new AccountBalance() { - var starting = new AccountBalance() - { - AccountId = this.Id.GetId(), - Balance = 100m, // Start new accounts with 100, we're pretty generous. - }; + AccountId = this.Id.GetId(), + Balance = 100m, // Start new accounts with 100, we're pretty generous. + }; - var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); - return balance; - } + var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); + return balance; + } - public async Task Withdraw(WithdrawRequest withdraw) + public async Task Withdraw(WithdrawRequest withdraw) + { + var starting = new AccountBalance() { - var starting = new AccountBalance() - { - AccountId = this.Id.GetId(), - Balance = 100m, // Start new accounts with 100, we're pretty generous. - }; + AccountId = this.Id.GetId(), + Balance = 100m, // Start new accounts with 100, we're pretty generous. + }; - var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); + var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); - // Throws Overdraft exception if the account doesn't have enough money. - var updated = this.bank.Withdraw(balance.Balance, withdraw.Amount); + // Throws Overdraft exception if the account doesn't have enough money. + var updated = this.bank.Withdraw(balance.Balance, withdraw.Amount); - balance.Balance = updated; - await this.StateManager.SetStateAsync("balance", balance); - } + balance.Balance = updated; + await this.StateManager.SetStateAsync("balance", balance); } -} +} \ No newline at end of file diff --git a/examples/Actor/DemoActor/Program.cs b/examples/Actor/DemoActor/Program.cs index 1d538b471..efb81adee 100644 --- a/examples/Actor/DemoActor/Program.cs +++ b/examples/Actor/DemoActor/Program.cs @@ -14,20 +14,19 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -namespace DemoActor +namespace DemoActor; + +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + CreateHostBuilder(args).Build().Run(); } -} + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs index f1165e3c7..6cde6953b 100644 --- a/examples/Actor/DemoActor/Startup.cs +++ b/examples/Actor/DemoActor/Startup.cs @@ -17,43 +17,42 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace DemoActor +namespace DemoActor; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - public void ConfigureServices(IServiceCollection services) + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddActors(options => { - services.AddSingleton(); - services.AddActors(options => - { - options.Actors.RegisterActor(); - }); - } + options.Actors.RegisterActor(); + }); + } - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseHsts(); - } - - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers(); - }); + app.UseHsts(); } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapActorsHandlers(); + }); } -} +} \ No newline at end of file diff --git a/examples/Actor/IDemoActor/IBankActor.cs b/examples/Actor/IDemoActor/IBankActor.cs index c495f027b..28242c522 100644 --- a/examples/Actor/IDemoActor/IBankActor.cs +++ b/examples/Actor/IDemoActor/IBankActor.cs @@ -15,32 +15,31 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace IDemoActor +namespace IDemoActor; + +public interface IBankActor : IActor { - public interface IBankActor : IActor - { - Task GetAccountBalance(); + Task GetAccountBalance(); - Task Withdraw(WithdrawRequest withdraw); - } + Task Withdraw(WithdrawRequest withdraw); +} - public class AccountBalance - { - public string AccountId { get; set; } +public class AccountBalance +{ + public string AccountId { get; set; } - public decimal Balance { get; set; } - } + public decimal Balance { get; set; } +} - public class WithdrawRequest - { - public decimal Amount { get; set; } - } +public class WithdrawRequest +{ + public decimal Amount { get; set; } +} - public class OverdraftException : Exception +public class OverdraftException : Exception +{ + public OverdraftException(decimal balance, decimal amount) + : base($"Your current balance is {balance:c} - that's not enough to withdraw {amount:c}.") { - public OverdraftException(decimal balance, decimal amount) - : base($"Your current balance is {balance:c} - that's not enough to withdraw {amount:c}.") - { - } } -} +} \ No newline at end of file diff --git a/examples/Actor/IDemoActor/IDemoActor.cs b/examples/Actor/IDemoActor/IDemoActor.cs index 6f2d32801..ce8ab7855 100644 --- a/examples/Actor/IDemoActor/IDemoActor.cs +++ b/examples/Actor/IDemoActor/IDemoActor.cs @@ -15,135 +15,134 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace IDemoActor +namespace IDemoActor; + +/// +/// Interface for Actor method. +/// +public interface IDemoActor : IActor { /// - /// Interface for Actor method. + /// Method to save data. /// - public interface IDemoActor : IActor - { - /// - /// Method to save data. - /// - /// DAta to save. - /// TTL of state key. - /// A task that represents the asynchronous save operation. - Task SaveData(MyData data, TimeSpan ttl); - - /// - /// Method to get data. - /// - /// A task that represents the asynchronous save operation. - Task GetData(); - - /// - /// A test method which throws exception. - /// - /// A task that represents the asynchronous save operation. - Task TestThrowException(); - - /// - /// A test method which validates calls for methods with no arguments and no return types. - /// - /// A task that represents the asynchronous save operation. - Task TestNoArgumentNoReturnType(); - - /// - /// Registers a reminder. - /// - /// A task that represents the asynchronous save operation. - Task RegisterReminder(); - - /// - /// Registers a reminder. - /// - /// TimeSpan that dictates when the reminder expires. - /// A task that represents the asynchronous save operation. - Task RegisterReminderWithTtl(TimeSpan ttl); - - /// - /// Unregisters the registered reminder. - /// - /// Task representing the operation. - Task UnregisterReminder(); - - /// - /// Registers a timer. - /// - /// A task that represents the asynchronous save operation. - Task RegisterTimer(); - - /// - /// Registers a timer. - /// - /// Optional TimeSpan that dictates when the timer expires. - /// A task that represents the asynchronous save operation. - Task RegisterTimerWithTtl(TimeSpan ttl); + /// DAta to save. + /// TTL of state key. + /// A task that represents the asynchronous save operation. + Task SaveData(MyData data, TimeSpan ttl); + + /// + /// Method to get data. + /// + /// A task that represents the asynchronous save operation. + Task GetData(); + + /// + /// A test method which throws exception. + /// + /// A task that represents the asynchronous save operation. + Task TestThrowException(); + + /// + /// A test method which validates calls for methods with no arguments and no return types. + /// + /// A task that represents the asynchronous save operation. + Task TestNoArgumentNoReturnType(); + + /// + /// Registers a reminder. + /// + /// A task that represents the asynchronous save operation. + Task RegisterReminder(); + + /// + /// Registers a reminder. + /// + /// TimeSpan that dictates when the reminder expires. + /// A task that represents the asynchronous save operation. + Task RegisterReminderWithTtl(TimeSpan ttl); + + /// + /// Unregisters the registered reminder. + /// + /// Task representing the operation. + Task UnregisterReminder(); + + /// + /// Registers a timer. + /// + /// A task that represents the asynchronous save operation. + Task RegisterTimer(); + + /// + /// Registers a timer. + /// + /// Optional TimeSpan that dictates when the timer expires. + /// A task that represents the asynchronous save operation. + Task RegisterTimerWithTtl(TimeSpan ttl); - /// - /// Registers a reminder with repetitions. - /// - /// The number of repetitions for which the reminder should be invoked. - /// A task that represents the asynchronous save operation. - Task RegisterReminderWithRepetitions(int repetitions); + /// + /// Registers a reminder with repetitions. + /// + /// The number of repetitions for which the reminder should be invoked. + /// A task that represents the asynchronous save operation. + Task RegisterReminderWithRepetitions(int repetitions); - /// - /// Registers a reminder with ttl and repetitions. - /// - /// TimeSpan that dictates when the timer expires. - /// The number of repetitions for which the reminder should be invoked. - /// A task that represents the asynchronous save operation. - Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions); - - /// - /// Gets the registered reminder. - /// - /// The name of the reminder. - /// A task that returns the reminder after completion. - Task GetReminder(); - - /// - /// Unregisters the registered timer. - /// - /// A task that represents the asynchronous save operation. - Task UnregisterTimer(); - } + /// + /// Registers a reminder with ttl and repetitions. + /// + /// TimeSpan that dictates when the timer expires. + /// The number of repetitions for which the reminder should be invoked. + /// A task that represents the asynchronous save operation. + Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions); + + /// + /// Gets the registered reminder. + /// + /// The name of the reminder. + /// A task that returns the reminder after completion. + Task GetReminder(); + + /// + /// Unregisters the registered timer. + /// + /// A task that represents the asynchronous save operation. + Task UnregisterTimer(); +} + +/// +/// Data Used by the Sample Actor. +/// +public class MyData +{ + /// + /// Gets or sets the value for PropertyA. + /// + public string PropertyA { get; set; } /// - /// Data Used by the Sample Actor. + /// Gets or sets the value for PropertyB. /// - public class MyData + public string PropertyB { get; set; } + + /// + public override string ToString() { - /// - /// Gets or sets the value for PropertyA. - /// - public string PropertyA { get; set; } - - /// - /// Gets or sets the value for PropertyB. - /// - public string PropertyB { get; set; } - - /// - public override string ToString() - { - var propAValue = this.PropertyA ?? "null"; - var propBValue = this.PropertyB ?? "null"; - return $"PropertyA: {propAValue}, PropertyB: {propBValue}"; - } + var propAValue = this.PropertyA ?? "null"; + var propBValue = this.PropertyB ?? "null"; + return $"PropertyA: {propAValue}, PropertyB: {propBValue}"; } +} - public class ActorReminderData - { - public string Name { get; set; } +public class ActorReminderData +{ + public string Name { get; set; } - public TimeSpan DueTime { get; set; } + public TimeSpan DueTime { get; set; } - public TimeSpan Period { get; set; } + public TimeSpan Period { get; set; } - public override string ToString() - { - return $"Name: {this.Name}, DueTime: {this.DueTime}, Period: {this.Period}"; - } + public override string ToString() + { + return $"Name: {this.Name}, DueTime: {this.DueTime}, Period: {this.Period}"; } -} +} \ No newline at end of file diff --git a/examples/AspNetCore/ControllerSample/Account.cs b/examples/AspNetCore/ControllerSample/Account.cs index ed2bddd21..bcd0d3501 100644 --- a/examples/AspNetCore/ControllerSample/Account.cs +++ b/examples/AspNetCore/ControllerSample/Account.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace ControllerSample +namespace ControllerSample; + +/// +/// Class representing an Account for samples. +/// +public class Account { /// - /// Class representing an Account for samples. + /// Gets or sets account id. /// - public class Account - { - /// - /// Gets or sets account id. - /// - public string Id { get; set; } + public string Id { get; set; } - /// - /// Gets or sets account balance. - /// - public decimal Balance { get; set; } - } + /// + /// Gets or sets account balance. + /// + public decimal Balance { get; set; } } \ No newline at end of file diff --git a/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs b/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs index 5b339288c..195c5ea50 100644 --- a/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs +++ b/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs @@ -13,286 +13,285 @@ using System.Linq; -namespace ControllerSample.Controllers +namespace ControllerSample.Controllers; + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Dapr; +using Dapr.AspNetCore; +using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +/// +/// Sample showing Dapr integration with controller. +/// +[ApiController] +public class SampleController : ControllerBase { - using System; - using System.Collections.Generic; - using System.Text; - using System.Text.Json; - using System.Threading.Tasks; - using Dapr; - using Dapr.AspNetCore; - using Dapr.Client; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Extensions.Logging; + /// + /// SampleController Constructor with logger injection + /// + /// + public SampleController(ILogger logger) + { + this.logger = logger; + } /// - /// Sample showing Dapr integration with controller. + /// State store name. /// - [ApiController] - public class SampleController : ControllerBase + public const string StoreName = "statestore"; + + private readonly ILogger logger; + + /// + /// Gets the account information as specified by the id. + /// + /// Account information for the id from Dapr state store. + /// Account information. + [HttpGet("{account}")] + public ActionResult Get([FromState(StoreName)] StateEntry account) { - /// - /// SampleController Constructor with logger injection - /// - /// - public SampleController(ILogger logger) + if (account.Value is null) { - this.logger = logger; + return this.NotFound(); } - /// - /// State store name. - /// - public const string StoreName = "statestore"; + return account.Value; + } - private readonly ILogger logger; + /// + /// Method for depositing to account as specified in transaction. + /// + /// Transaction info. + /// State client to interact with Dapr runtime. + /// A representing the result of the asynchronous operation. + /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. + [Topic("pubsub", "deposit", "amountDeadLetterTopic", false)] + [HttpPost("deposit")] + public async Task> Deposit(Transaction transaction, [FromServices] DaprClient daprClient) + { + // Example reading cloudevent properties from the headers + var headerEntries = Request.Headers.Aggregate("", (current, header) => current + ($"------- Header: {header.Key} : {header.Value}" + Environment.NewLine)); - /// - /// Gets the account information as specified by the id. - /// - /// Account information for the id from Dapr state store. - /// Account information. - [HttpGet("{account}")] - public ActionResult Get([FromState(StoreName)] StateEntry account) - { - if (account.Value is null) - { - return this.NotFound(); - } + logger.LogInformation(headerEntries); - return account.Value; - } + logger.LogInformation("Enter deposit"); + var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); + state.Value ??= new Account() { Id = transaction.Id, }; + logger.LogInformation("Id is {0}, the amount to be deposited is {1}", transaction.Id, transaction.Amount); - /// - /// Method for depositing to account as specified in transaction. - /// - /// Transaction info. - /// State client to interact with Dapr runtime. - /// A representing the result of the asynchronous operation. - /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. - [Topic("pubsub", "deposit", "amountDeadLetterTopic", false)] - [HttpPost("deposit")] - public async Task> Deposit(Transaction transaction, [FromServices] DaprClient daprClient) + if (transaction.Amount < 0m) { - // Example reading cloudevent properties from the headers - var headerEntries = Request.Headers.Aggregate("", (current, header) => current + ($"------- Header: {header.Key} : {header.Value}" + Environment.NewLine)); - - logger.LogInformation(headerEntries); + return BadRequest(new { statusCode = 400, message = "bad request" }); + } - logger.LogInformation("Enter deposit"); - var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); - state.Value ??= new Account() { Id = transaction.Id, }; - logger.LogInformation("Id is {0}, the amount to be deposited is {1}", transaction.Id, transaction.Amount); + state.Value.Balance += transaction.Amount; + logger.LogInformation("Balance for Id {0} is {1}", state.Value.Id, state.Value.Balance); + await state.SaveAsync(); + return state.Value; + } - if (transaction.Amount < 0m) - { - return BadRequest(new { statusCode = 400, message = "bad request" }); - } + /// + /// Method for depositing multiple times to the account as specified in transaction. + /// + /// List of entries of type BulkMessageModel received from dapr. + /// State client to interact with Dapr runtime. + /// A representing the result of the asynchronous operation. + /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. + [Topic("pubsub", "multideposit", "amountDeadLetterTopic", false)] + [BulkSubscribe("multideposit", 500, 2000)] + [HttpPost("multideposit")] + public async Task> MultiDeposit([FromBody] + BulkSubscribeMessage> + bulkMessage, [FromServices] DaprClient daprClient) + { + logger.LogInformation("Enter bulk deposit"); - state.Value.Balance += transaction.Amount; - logger.LogInformation("Balance for Id {0} is {1}", state.Value.Id, state.Value.Balance); - await state.SaveAsync(); - return state.Value; - } + List entries = new List(); - /// - /// Method for depositing multiple times to the account as specified in transaction. - /// - /// List of entries of type BulkMessageModel received from dapr. - /// State client to interact with Dapr runtime. - /// A representing the result of the asynchronous operation. - /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. - [Topic("pubsub", "multideposit", "amountDeadLetterTopic", false)] - [BulkSubscribe("multideposit", 500, 2000)] - [HttpPost("multideposit")] - public async Task> MultiDeposit([FromBody] - BulkSubscribeMessage> - bulkMessage, [FromServices] DaprClient daprClient) + foreach (var entry in bulkMessage.Entries) { - logger.LogInformation("Enter bulk deposit"); + try + { + var transaction = entry.Event.Data; - List entries = new List(); + var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); + state.Value ??= new Account() { Id = transaction.Id, }; + logger.LogInformation("Id is {0}, the amount to be deposited is {1}", + transaction.Id, transaction.Amount); - foreach (var entry in bulkMessage.Entries) - { - try + if (transaction.Amount < 0m) { - var transaction = entry.Event.Data; - - var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); - state.Value ??= new Account() { Id = transaction.Id, }; - logger.LogInformation("Id is {0}, the amount to be deposited is {1}", - transaction.Id, transaction.Amount); - - if (transaction.Amount < 0m) - { - return BadRequest(new { statusCode = 400, message = "bad request" }); - } - - state.Value.Balance += transaction.Amount; - logger.LogInformation("Balance is {0}", state.Value.Balance); - await state.SaveAsync(); - entries.Add( - new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.SUCCESS)); + return BadRequest(new { statusCode = 400, message = "bad request" }); } - catch (Exception e) - { - logger.LogError(e.Message); - entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.RETRY)); - } - } - - return new BulkSubscribeAppResponse(entries); - } - /// - /// Method for viewing the error message when the deposit/withdrawal amounts - /// are negative. - /// - /// Transaction info. - [Topic("pubsub", "amountDeadLetterTopic")] - [HttpPost("deadLetterTopicRoute")] - public ActionResult ViewErrorMessage(Transaction transaction) - { - logger.LogInformation("The amount cannot be negative: {0}", transaction.Amount); - return Ok(); - } - - /// - /// Method for withdrawing from account as specified in transaction. - /// - /// Transaction info. - /// State client to interact with Dapr runtime. - /// A representing the result of the asynchronous operation. - /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. - [Topic("pubsub", "withdraw", "amountDeadLetterTopic", false)] - [HttpPost("withdraw")] - public async Task> Withdraw(Transaction transaction, [FromServices] DaprClient daprClient) - { - logger.LogInformation("Enter withdraw method..."); - var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); - logger.LogInformation("Id is {0}, the amount to be withdrawn is {1}", transaction.Id, transaction.Amount); - - if (state.Value == null) - { - return this.NotFound(); + state.Value.Balance += transaction.Amount; + logger.LogInformation("Balance is {0}", state.Value.Balance); + await state.SaveAsync(); + entries.Add( + new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.SUCCESS)); } - - if (transaction.Amount < 0m) + catch (Exception e) { - return BadRequest(new { statusCode = 400, message = "bad request" }); + logger.LogError(e.Message); + entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.RETRY)); } - - state.Value.Balance -= transaction.Amount; - logger.LogInformation("Balance is {0}", state.Value.Balance); - await state.SaveAsync(); - return state.Value; } - /// - /// Method for withdrawing from account as specified in transaction. - /// - /// Transaction info. - /// State client to interact with Dapr runtime. - /// A representing the result of the asynchronous operation. - /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. - [Topic("pubsub", "withdraw", "event.type ==\"withdraw.v2\"", 1)] - [HttpPost("withdraw.v2")] - public async Task> WithdrawV2(TransactionV2 transaction, - [FromServices] DaprClient daprClient) - { - logger.LogInformation("Enter withdraw.v2"); - if (transaction.Channel == "mobile" && transaction.Amount > 10000) - { - return this.Unauthorized("mobile transactions for large amounts are not permitted."); - } + return new BulkSubscribeAppResponse(entries); + } - var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); + /// + /// Method for viewing the error message when the deposit/withdrawal amounts + /// are negative. + /// + /// Transaction info. + [Topic("pubsub", "amountDeadLetterTopic")] + [HttpPost("deadLetterTopicRoute")] + public ActionResult ViewErrorMessage(Transaction transaction) + { + logger.LogInformation("The amount cannot be negative: {0}", transaction.Amount); + return Ok(); + } - if (state.Value == null) - { - return this.NotFound(); - } + /// + /// Method for withdrawing from account as specified in transaction. + /// + /// Transaction info. + /// State client to interact with Dapr runtime. + /// A representing the result of the asynchronous operation. + /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. + [Topic("pubsub", "withdraw", "amountDeadLetterTopic", false)] + [HttpPost("withdraw")] + public async Task> Withdraw(Transaction transaction, [FromServices] DaprClient daprClient) + { + logger.LogInformation("Enter withdraw method..."); + var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); + logger.LogInformation("Id is {0}, the amount to be withdrawn is {1}", transaction.Id, transaction.Amount); - state.Value.Balance -= transaction.Amount; - await state.SaveAsync(); - return state.Value; + if (state.Value == null) + { + return this.NotFound(); } - /// - /// Method for depositing to account as specified in transaction via a raw message. - /// - /// Transaction info. - /// State client to interact with Dapr runtime. - /// A representing the result of the asynchronous operation. - /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. - [Topic("pubsub", "rawDeposit", true)] - [HttpPost("rawDeposit")] - public async Task> RawDeposit([FromBody] JsonDocument rawTransaction, - [FromServices] DaprClient daprClient) + if (transaction.Amount < 0m) { - var transactionString = rawTransaction.RootElement.GetProperty("data_base64").GetString(); - logger.LogInformation( - $"Enter deposit: {transactionString} - {Encoding.UTF8.GetString(Convert.FromBase64String(transactionString))}"); - var transactionJson = JsonSerializer.Deserialize(Convert.FromBase64String(transactionString)); - var transaction = - JsonSerializer.Deserialize(transactionJson.RootElement.GetProperty("data").GetRawText()); - var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); - state.Value ??= new Account() { Id = transaction.Id, }; - logger.LogInformation("Id is {0}, the amount to be deposited is {1}", transaction.Id, transaction.Amount); - - if (transaction.Amount < 0m) - { - return BadRequest(new { statusCode = 400, message = "bad request" }); - } - - state.Value.Balance += transaction.Amount; - logger.LogInformation("Balance is {0}", state.Value.Balance); - await state.SaveAsync(); - return state.Value; + return BadRequest(new { statusCode = 400, message = "bad request" }); } - /// - /// Method for returning a BadRequest result which will cause Dapr sidecar to throw an RpcException - /// - [HttpPost("throwException")] - public async Task> ThrowException(Transaction transaction, - [FromServices] DaprClient daprClient) + state.Value.Balance -= transaction.Amount; + logger.LogInformation("Balance is {0}", state.Value.Balance); + await state.SaveAsync(); + return state.Value; + } + + /// + /// Method for withdrawing from account as specified in transaction. + /// + /// Transaction info. + /// State client to interact with Dapr runtime. + /// A representing the result of the asynchronous operation. + /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. + [Topic("pubsub", "withdraw", "event.type ==\"withdraw.v2\"", 1)] + [HttpPost("withdraw.v2")] + public async Task> WithdrawV2(TransactionV2 transaction, + [FromServices] DaprClient daprClient) + { + logger.LogInformation("Enter withdraw.v2"); + if (transaction.Channel == "mobile" && transaction.Amount > 10000) { - logger.LogInformation("Enter ThrowException"); - var task = Task.Delay(10); - await task; - return BadRequest(new { statusCode = 400, message = "bad request" }); + return this.Unauthorized("mobile transactions for large amounts are not permitted."); } - /// - /// - /// Method which uses for binding this endpoint to a subscription. - /// - /// - /// This endpoint will be bound to a subscription where the topic name is the value of the environment variable 'CUSTOM_TOPIC' - /// and the pubsub name is the value of the environment variable 'CUSTOM_PUBSUB'. - /// - /// - [CustomTopic("%CUSTOM_PUBSUB%", "%CUSTOM_TOPIC%")] - [HttpPost("exampleCustomTopic")] - public ActionResult ExampleCustomTopic(Transaction transaction) + var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); + + if (state.Value == null) { - return Ok(); + return this.NotFound(); } - /// - /// Method which uses for binding this endpoint to a subscription and adds routingkey metadata. - /// - /// - /// - [Topic("pubsub", "topicmetadata")] - [TopicMetadata("routingKey", "keyA")] - [HttpPost("examplecustomtopicmetadata")] - public ActionResult ExampleCustomTopicMetadata(Transaction transaction) + state.Value.Balance -= transaction.Amount; + await state.SaveAsync(); + return state.Value; + } + + /// + /// Method for depositing to account as specified in transaction via a raw message. + /// + /// Transaction info. + /// State client to interact with Dapr runtime. + /// A representing the result of the asynchronous operation. + /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. + [Topic("pubsub", "rawDeposit", true)] + [HttpPost("rawDeposit")] + public async Task> RawDeposit([FromBody] JsonDocument rawTransaction, + [FromServices] DaprClient daprClient) + { + var transactionString = rawTransaction.RootElement.GetProperty("data_base64").GetString(); + logger.LogInformation( + $"Enter deposit: {transactionString} - {Encoding.UTF8.GetString(Convert.FromBase64String(transactionString))}"); + var transactionJson = JsonSerializer.Deserialize(Convert.FromBase64String(transactionString)); + var transaction = + JsonSerializer.Deserialize(transactionJson.RootElement.GetProperty("data").GetRawText()); + var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); + state.Value ??= new Account() { Id = transaction.Id, }; + logger.LogInformation("Id is {0}, the amount to be deposited is {1}", transaction.Id, transaction.Amount); + + if (transaction.Amount < 0m) { - return Ok(); + return BadRequest(new { statusCode = 400, message = "bad request" }); } + + state.Value.Balance += transaction.Amount; + logger.LogInformation("Balance is {0}", state.Value.Balance); + await state.SaveAsync(); + return state.Value; + } + + /// + /// Method for returning a BadRequest result which will cause Dapr sidecar to throw an RpcException + /// + [HttpPost("throwException")] + public async Task> ThrowException(Transaction transaction, + [FromServices] DaprClient daprClient) + { + logger.LogInformation("Enter ThrowException"); + var task = Task.Delay(10); + await task; + return BadRequest(new { statusCode = 400, message = "bad request" }); + } + + /// + /// + /// Method which uses for binding this endpoint to a subscription. + /// + /// + /// This endpoint will be bound to a subscription where the topic name is the value of the environment variable 'CUSTOM_TOPIC' + /// and the pubsub name is the value of the environment variable 'CUSTOM_PUBSUB'. + /// + /// + [CustomTopic("%CUSTOM_PUBSUB%", "%CUSTOM_TOPIC%")] + [HttpPost("exampleCustomTopic")] + public ActionResult ExampleCustomTopic(Transaction transaction) + { + return Ok(); + } + + /// + /// Method which uses for binding this endpoint to a subscription and adds routingkey metadata. + /// + /// + /// + [Topic("pubsub", "topicmetadata")] + [TopicMetadata("routingKey", "keyA")] + [HttpPost("examplecustomtopicmetadata")] + public ActionResult ExampleCustomTopicMetadata(Transaction transaction) + { + return Ok(); } -} +} \ No newline at end of file diff --git a/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs b/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs index 5c9996aea..eb96ba894 100644 --- a/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs +++ b/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs @@ -13,33 +13,32 @@ using Dapr.AspNetCore; -namespace ControllerSample +namespace ControllerSample; + +using System; +using Dapr; + +/// +/// Sample custom implementation that returns topic metadata from environment variables. +/// +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class CustomTopicAttribute : Attribute, ITopicMetadata { - using System; - using Dapr; - - /// - /// Sample custom implementation that returns topic metadata from environment variables. - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class CustomTopicAttribute : Attribute, ITopicMetadata + public CustomTopicAttribute(string pubsubName, string name) { - public CustomTopicAttribute(string pubsubName, string name) - { - this.PubsubName = Environment.ExpandEnvironmentVariables(pubsubName); - this.Name = Environment.ExpandEnvironmentVariables(name); - } + this.PubsubName = Environment.ExpandEnvironmentVariables(pubsubName); + this.Name = Environment.ExpandEnvironmentVariables(name); + } - /// - public string PubsubName { get; } + /// + public string PubsubName { get; } - /// - public string Name { get; } + /// + public string Name { get; } - /// - public new string Match { get; } + /// + public new string Match { get; } - /// - public int Priority { get; } - } -} + /// + public int Priority { get; } +} \ No newline at end of file diff --git a/examples/AspNetCore/ControllerSample/Program.cs b/examples/AspNetCore/ControllerSample/Program.cs index 251d11fc1..68eb85cc5 100644 --- a/examples/AspNetCore/ControllerSample/Program.cs +++ b/examples/AspNetCore/ControllerSample/Program.cs @@ -11,35 +11,34 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace ControllerSample -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; +namespace ControllerSample; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +/// +/// Controller Sample. +/// +public class Program +{ /// - /// Controller Sample. + /// Main for Controller Sample. /// - public class Program + /// Arguments. + public static void Main(string[] args) { - /// - /// Main for Controller Sample. - /// - /// Arguments. - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - /// - /// Creates WebHost Builder. - /// - /// Arguments. - /// Returns IHostbuilder. - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + CreateHostBuilder(args).Build().Run(); } -} + + /// + /// Creates WebHost Builder. + /// + /// Arguments. + /// Returns IHostbuilder. + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/examples/AspNetCore/ControllerSample/Startup.cs b/examples/AspNetCore/ControllerSample/Startup.cs index ddc6d1c5f..17afa06b5 100644 --- a/examples/AspNetCore/ControllerSample/Startup.cs +++ b/examples/AspNetCore/ControllerSample/Startup.cs @@ -16,68 +16,67 @@ using Dapr.AspNetCore; -namespace ControllerSample -{ - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; +namespace ControllerSample; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +/// +/// Startup class. +/// +public class Startup +{ /// - /// Startup class. + /// Initializes a new instance of the class. /// - public class Startup + /// Configuration. + public Startup(IConfiguration configuration) { - /// - /// Initializes a new instance of the class. - /// - /// Configuration. - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } - /// - /// Gets the configuration. - /// - public IConfiguration Configuration { get; } + /// + /// Gets the configuration. + /// + public IConfiguration Configuration { get; } - /// - /// Configures Services. - /// - /// Service Collection. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers().AddDapr(); - } + /// + /// Configures Services. + /// + /// Service Collection. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers().AddDapr(); + } - /// - /// Configures Application Builder and WebHost environment. - /// - /// Application builder. - /// Webhost environment. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + /// + /// Configures Application Builder and WebHost environment. + /// + /// Application builder. + /// Webhost environment. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseCloudEvents(new CloudEventsMiddlewareOptions - { - ForwardCloudEventPropertiesAsHeaders = true - }); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true + }); - app.UseAuthorization(); + app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapSubscribeHandler(); - endpoints.MapControllers(); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapSubscribeHandler(); + endpoints.MapControllers(); + }); } -} +} \ No newline at end of file diff --git a/examples/AspNetCore/ControllerSample/Transaction.cs b/examples/AspNetCore/ControllerSample/Transaction.cs index 2d6f5a5aa..5b7a1c868 100644 --- a/examples/AspNetCore/ControllerSample/Transaction.cs +++ b/examples/AspNetCore/ControllerSample/Transaction.cs @@ -11,24 +11,23 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace ControllerSample -{ - using System.ComponentModel.DataAnnotations; +namespace ControllerSample; + +using System.ComponentModel.DataAnnotations; +/// +/// Represents a transaction used by sample code. +/// +public class Transaction +{ /// - /// Represents a transaction used by sample code. + /// Gets or sets account id for the transaction. /// - public class Transaction - { - /// - /// Gets or sets account id for the transaction. - /// - [Required] - public string Id { get; set; } + [Required] + public string Id { get; set; } - /// - /// Gets or sets amount for the transaction. - /// + /// Gets or sets amount for the transaction. + /// +/// Represents a transaction used by sample code. +/// +public class TransactionV2 +{ /// - /// Represents a transaction used by sample code. + /// Gets or sets account id for the transaction. /// - public class TransactionV2 - { - /// - /// Gets or sets account id for the transaction. - /// - [Required] - public string Id { get; set; } + [Required] + public string Id { get; set; } - /// - /// Gets or sets amount for the transaction. - /// - [Range(0, double.MaxValue)] - public decimal Amount { get; set; } + /// + /// Gets or sets amount for the transaction. + /// + [Range(0, double.MaxValue)] + public decimal Amount { get; set; } - /// - /// Gets or sets channel from which this transaction was received. - /// - [Required] - public string Channel { get; set; } - } + /// + /// Gets or sets channel from which this transaction was received. + /// + [Required] + public string Channel { get; set; } } \ No newline at end of file diff --git a/examples/AspNetCore/GrpcServiceSample/Models/Account.cs b/examples/AspNetCore/GrpcServiceSample/Models/Account.cs index 0b6f810d1..91746beff 100644 --- a/examples/AspNetCore/GrpcServiceSample/Models/Account.cs +++ b/examples/AspNetCore/GrpcServiceSample/Models/Account.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace GrpcServiceSample.Models +namespace GrpcServiceSample.Models; + +/// +/// Class representing an Account for samples. +/// +public class Account { /// - /// Class representing an Account for samples. + /// Gets or sets account id. /// - public class Account - { - /// - /// Gets or sets account id. - /// - public string Id { get; set; } + public string Id { get; set; } - /// - /// Gets or sets account balance. - /// - public decimal Balance { get; set; } - } -} + /// + /// Gets or sets account balance. + /// + public decimal Balance { get; set; } +} \ No newline at end of file diff --git a/examples/AspNetCore/GrpcServiceSample/Models/GetAccountInput.cs b/examples/AspNetCore/GrpcServiceSample/Models/GetAccountInput.cs index bcb835bd6..7d7f59fc4 100644 --- a/examples/AspNetCore/GrpcServiceSample/Models/GetAccountInput.cs +++ b/examples/AspNetCore/GrpcServiceSample/Models/GetAccountInput.cs @@ -11,16 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace GrpcServiceSample.Models +namespace GrpcServiceSample.Models; + +/// +/// BankService GetAccount input model +/// +public class GetAccountInput { /// - /// BankService GetAccount input model + /// Id of account /// - public class GetAccountInput - { - /// - /// Id of account - /// - public string Id { get; set; } - } -} + public string Id { get; set; } +} \ No newline at end of file diff --git a/examples/AspNetCore/GrpcServiceSample/Models/Transaction.cs b/examples/AspNetCore/GrpcServiceSample/Models/Transaction.cs index 7a1ef7cbd..a13e5c5eb 100644 --- a/examples/AspNetCore/GrpcServiceSample/Models/Transaction.cs +++ b/examples/AspNetCore/GrpcServiceSample/Models/Transaction.cs @@ -13,23 +13,22 @@ using System.ComponentModel.DataAnnotations; -namespace GrpcServiceSample.Models +namespace GrpcServiceSample.Models; + +/// +/// Represents a transaction used by sample code. +/// +public class Transaction { /// - /// Represents a transaction used by sample code. + /// Gets or sets account id for the transaction. /// - public class Transaction - { - /// - /// Gets or sets account id for the transaction. - /// - [Required] - public string Id { get; set; } + [Required] + public string Id { get; set; } - /// - /// Gets or sets amount for the transaction. - /// - [Range(0, double.MaxValue)] - public decimal Amount { get; set; } - } -} + /// + /// Gets or sets amount for the transaction. + /// + [Range(0, double.MaxValue)] + public decimal Amount { get; set; } +} \ No newline at end of file diff --git a/examples/AspNetCore/GrpcServiceSample/Program.cs b/examples/AspNetCore/GrpcServiceSample/Program.cs index dbec5ba2c..d7af01704 100644 --- a/examples/AspNetCore/GrpcServiceSample/Program.cs +++ b/examples/AspNetCore/GrpcServiceSample/Program.cs @@ -15,39 +15,38 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Hosting; -namespace GrpcServiceSample +namespace GrpcServiceSample; + +/// +/// GrpcService Sample +/// +public class Program { /// - /// GrpcService Sample + /// Entry point /// - public class Program + /// + public static void Main(string[] args) { - /// - /// Entry point - /// - /// - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } + CreateHostBuilder(args).Build().Run(); + } - /// - /// Creates WebHost Builder. - /// - /// - /// - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => + /// + /// Creates WebHost Builder. + /// + /// + /// + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureKestrel(options => { - webBuilder.ConfigureKestrel(options => - { - // Setup a HTTP/2 endpoint without TLS. - options.ListenLocalhost(5050, o => o.Protocols = - HttpProtocols.Http2); - }); - - webBuilder.UseStartup(); + // Setup a HTTP/2 endpoint without TLS. + options.ListenLocalhost(5050, o => o.Protocols = + HttpProtocols.Http2); }); - } -} + + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs b/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs index 9518fd610..05b037841 100644 --- a/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs +++ b/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs @@ -22,158 +22,157 @@ using GrpcServiceSample.Generated; using Microsoft.Extensions.Logging; -namespace GrpcServiceSample.Services +namespace GrpcServiceSample.Services; + +/// +/// BankAccount gRPC service +/// +public class BankingService : AppCallback.AppCallbackBase { /// - /// BankAccount gRPC service + /// State store name. /// - public class BankingService : AppCallback.AppCallbackBase - { - /// - /// State store name. - /// - public const string StoreName = "statestore"; + public const string StoreName = "statestore"; - private readonly ILogger _logger; - private readonly DaprClient _daprClient; + private readonly ILogger _logger; + private readonly DaprClient _daprClient; - /// - /// Constructor - /// - /// - /// - public BankingService(DaprClient daprClient, ILogger logger) - { - _daprClient = daprClient; - _logger = logger; - } + /// + /// Constructor + /// + /// + /// + public BankingService(DaprClient daprClient, ILogger logger) + { + _daprClient = daprClient; + _logger = logger; + } - readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - /// - /// implement OnIvoke to support getaccount, deposit and withdraw - /// - /// - /// - /// - public override async Task OnInvoke(InvokeRequest request, ServerCallContext context) + /// + /// implement OnIvoke to support getaccount, deposit and withdraw + /// + /// + /// + /// + public override async Task OnInvoke(InvokeRequest request, ServerCallContext context) + { + var response = new InvokeResponse(); + switch (request.Method) { - var response = new InvokeResponse(); - switch (request.Method) - { - case "getaccount": - var input = request.Data.Unpack(); - var output = await GetAccount(input, context); - response.Data = Any.Pack(output); - break; - case "deposit": - case "withdraw": - var transaction = request.Data.Unpack(); - var account = request.Method == "deposit" ? - await Deposit(transaction, context) : - await Withdraw(transaction, context); - response.Data = Any.Pack(account); - break; - default: - break; - } - return response; + case "getaccount": + var input = request.Data.Unpack(); + var output = await GetAccount(input, context); + response.Data = Any.Pack(output); + break; + case "deposit": + case "withdraw": + var transaction = request.Data.Unpack(); + var account = request.Method == "deposit" ? + await Deposit(transaction, context) : + await Withdraw(transaction, context); + response.Data = Any.Pack(account); + break; + default: + break; } + return response; + } - /// - /// implement ListTopicSubscriptions to register deposit and withdraw subscriber - /// - /// - /// - /// - public override Task ListTopicSubscriptions(Empty request, ServerCallContext context) + /// + /// implement ListTopicSubscriptions to register deposit and withdraw subscriber + /// + /// + /// + /// + public override Task ListTopicSubscriptions(Empty request, ServerCallContext context) + { + var result = new ListTopicSubscriptionsResponse(); + result.Subscriptions.Add(new TopicSubscription { - var result = new ListTopicSubscriptionsResponse(); - result.Subscriptions.Add(new TopicSubscription - { - PubsubName = "pubsub", - Topic = "deposit" - }); - result.Subscriptions.Add(new TopicSubscription - { - PubsubName = "pubsub", - Topic = "withdraw" - }); - return Task.FromResult(result); - } + PubsubName = "pubsub", + Topic = "deposit" + }); + result.Subscriptions.Add(new TopicSubscription + { + PubsubName = "pubsub", + Topic = "withdraw" + }); + return Task.FromResult(result); + } - /// - /// implement OnTopicEvent to handle deposit and withdraw event - /// - /// - /// - /// - public override async Task OnTopicEvent(TopicEventRequest request, ServerCallContext context) + /// + /// implement OnTopicEvent to handle deposit and withdraw event + /// + /// + /// + /// + public override async Task OnTopicEvent(TopicEventRequest request, ServerCallContext context) + { + if (request.PubsubName == "pubsub") { - if (request.PubsubName == "pubsub") + var input = JsonSerializer.Deserialize(request.Data.ToStringUtf8(), this.jsonOptions); + var transaction = new GrpcServiceSample.Generated.Transaction() { Id = input.Id, Amount = (int)input.Amount, }; + if (request.Topic == "deposit") { - var input = JsonSerializer.Deserialize(request.Data.ToStringUtf8(), this.jsonOptions); - var transaction = new GrpcServiceSample.Generated.Transaction() { Id = input.Id, Amount = (int)input.Amount, }; - if (request.Topic == "deposit") - { - await Deposit(transaction, context); - } - else - { - await Withdraw(transaction, context); - } + await Deposit(transaction, context); + } + else + { + await Withdraw(transaction, context); } - - return new TopicEventResponse(); } - /// - /// GetAccount - /// - /// - /// - /// - public async Task GetAccount(GetAccountRequest input, ServerCallContext context) - { - var state = await _daprClient.GetStateEntryAsync(StoreName, input.Id); - return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; - } + return new TopicEventResponse(); + } - /// - /// Deposit - /// - /// - /// - /// - public async Task Deposit(GrpcServiceSample.Generated.Transaction transaction, ServerCallContext context) - { - _logger.LogDebug("Enter deposit"); - var state = await _daprClient.GetStateEntryAsync(StoreName, transaction.Id); - state.Value ??= new Models.Account() { Id = transaction.Id, }; - state.Value.Balance += transaction.Amount; - await state.SaveAsync(); - return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; - } + /// + /// GetAccount + /// + /// + /// + /// + public async Task GetAccount(GetAccountRequest input, ServerCallContext context) + { + var state = await _daprClient.GetStateEntryAsync(StoreName, input.Id); + return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; + } - /// - /// Withdraw - /// - /// - /// - /// - public async Task Withdraw(GrpcServiceSample.Generated.Transaction transaction, ServerCallContext context) - { - _logger.LogDebug("Enter withdraw"); - var state = await _daprClient.GetStateEntryAsync(StoreName, transaction.Id); + /// + /// Deposit + /// + /// + /// + /// + public async Task Deposit(GrpcServiceSample.Generated.Transaction transaction, ServerCallContext context) + { + _logger.LogDebug("Enter deposit"); + var state = await _daprClient.GetStateEntryAsync(StoreName, transaction.Id); + state.Value ??= new Models.Account() { Id = transaction.Id, }; + state.Value.Balance += transaction.Amount; + await state.SaveAsync(); + return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; + } - if (state.Value == null) - { - throw new Exception($"NotFound: {transaction.Id}"); - } + /// + /// Withdraw + /// + /// + /// + /// + public async Task Withdraw(GrpcServiceSample.Generated.Transaction transaction, ServerCallContext context) + { + _logger.LogDebug("Enter withdraw"); + var state = await _daprClient.GetStateEntryAsync(StoreName, transaction.Id); - state.Value.Balance -= transaction.Amount; - await state.SaveAsync(); - return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; + if (state.Value == null) + { + throw new Exception($"NotFound: {transaction.Id}"); } + + state.Value.Balance -= transaction.Amount; + await state.SaveAsync(); + return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; } -} +} \ No newline at end of file diff --git a/examples/AspNetCore/GrpcServiceSample/Startup.cs b/examples/AspNetCore/GrpcServiceSample/Startup.cs index 4aa5ac7d3..f2c606496 100644 --- a/examples/AspNetCore/GrpcServiceSample/Startup.cs +++ b/examples/AspNetCore/GrpcServiceSample/Startup.cs @@ -19,47 +19,46 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace GrpcServiceSample +namespace GrpcServiceSample; + +/// +/// Startup class. +/// +public class Startup { /// - /// Startup class. + /// Configure Services /// - public class Startup + /// + public void ConfigureServices(IServiceCollection services) { - /// - /// Configure Services - /// - /// - public void ConfigureServices(IServiceCollection services) - { - services.AddGrpc(); + services.AddGrpc(); - services.AddDaprClient(); - } + services.AddDaprClient(); + } - /// - /// Configure app - /// - /// - /// - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + /// + /// Configure app + /// + /// + /// + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGrpcService(); + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); - endpoints.MapGet("/", async context => - { - await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); - }); + endpoints.MapGet("/", async context => + { + await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); - } + }); } -} +} \ No newline at end of file diff --git a/examples/AspNetCore/RoutingSample/Account.cs b/examples/AspNetCore/RoutingSample/Account.cs index ede662b10..5aac78ce6 100644 --- a/examples/AspNetCore/RoutingSample/Account.cs +++ b/examples/AspNetCore/RoutingSample/Account.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace RoutingSample +namespace RoutingSample; + +/// +/// Class representing an Account for samples. +/// +public class Account { /// - /// Class representing an Account for samples. + /// Gets or sets account id. /// - public class Account - { - /// - /// Gets or sets account id. - /// - public string Id { get; set; } + public string Id { get; set; } - /// - /// Gets or sets account balance. - /// - public decimal Balance { get; set; } - } + /// + /// Gets or sets account balance. + /// + public decimal Balance { get; set; } } \ No newline at end of file diff --git a/examples/AspNetCore/RoutingSample/Program.cs b/examples/AspNetCore/RoutingSample/Program.cs index 505f58d10..6aa86d120 100644 --- a/examples/AspNetCore/RoutingSample/Program.cs +++ b/examples/AspNetCore/RoutingSample/Program.cs @@ -11,35 +11,34 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace RoutingSample -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; +namespace RoutingSample; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +/// +/// Controller Sample. +/// +public static class Program +{ /// - /// Controller Sample. + /// Main for Controller Sample. /// - public static class Program + /// Arguments. + public static void Main(string[] args) { - /// - /// Main for Controller Sample. - /// - /// Arguments. - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - /// - /// Creates WebHost Builder. - /// - /// Arguments. - /// Returns IHostbuilder. - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + CreateHostBuilder(args).Build().Run(); } -} + + /// + /// Creates WebHost Builder. + /// + /// Arguments. + /// Returns IHostbuilder. + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/examples/AspNetCore/RoutingSample/Startup.cs b/examples/AspNetCore/RoutingSample/Startup.cs index 3d71e9e1f..3e7d7b0c5 100644 --- a/examples/AspNetCore/RoutingSample/Startup.cs +++ b/examples/AspNetCore/RoutingSample/Startup.cs @@ -11,254 +11,253 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace RoutingSample +namespace RoutingSample; + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; +using Dapr; +using Dapr.AspNetCore; +using Dapr.Client; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +/// +/// Startup class. +/// +public class Startup { - using System; - using System.Collections.Generic; - using System.Text.Json; - using System.Threading.Tasks; - using Dapr; - using Dapr.AspNetCore; - using Dapr.Client; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; + /// + /// State store name. + /// + public const string StoreName = "statestore"; + + /// + /// Pubsub component name. "pubsub" is name of the default pub/sub configured by the Dapr CLI. + /// + public const string PubsubName = "pubsub"; /// - /// Startup class. + /// Initializes a new instance of the class. /// - public class Startup + /// Configuration. + public Startup(IConfiguration configuration) { - /// - /// State store name. - /// - public const string StoreName = "statestore"; - - /// - /// Pubsub component name. "pubsub" is name of the default pub/sub configured by the Dapr CLI. - /// - public const string PubsubName = "pubsub"; - - /// - /// Initializes a new instance of the class. - /// - /// Configuration. - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } + + /// + /// Gets the configuration. + /// + public IConfiguration Configuration { get; } - /// - /// Gets the configuration. - /// - public IConfiguration Configuration { get; } + /// + /// Configures Services. + /// + /// Service Collection. + public void ConfigureServices(IServiceCollection services) + { + services.AddDaprClient(); - /// - /// Configures Services. - /// - /// Service Collection. - public void ConfigureServices(IServiceCollection services) + services.AddSingleton(new JsonSerializerOptions() { - services.AddDaprClient(); - - services.AddSingleton(new JsonSerializerOptions() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true, - }); - } + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + }); + } - /// - /// Configures Application Builder and WebHost environment. - /// - /// Application builder. - /// Webhost environment. - /// Options for JSON serialization. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, JsonSerializerOptions serializerOptions, - ILogger logger) + /// + /// Configures Application Builder and WebHost environment. + /// + /// Application builder. + /// Webhost environment. + /// Options for JSON serialization. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, JsonSerializerOptions serializerOptions, + ILogger logger) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseCloudEvents(); + app.UseCloudEvents(); - app.UseEndpoints(endpoints => - { - endpoints.MapSubscribeHandler(); + app.UseEndpoints(endpoints => + { + endpoints.MapSubscribeHandler(); - var depositTopicOptions = new TopicOptions(); - depositTopicOptions.PubsubName = PubsubName; - depositTopicOptions.Name = "deposit"; - depositTopicOptions.DeadLetterTopic = "amountDeadLetterTopic"; + var depositTopicOptions = new TopicOptions(); + depositTopicOptions.PubsubName = PubsubName; + depositTopicOptions.Name = "deposit"; + depositTopicOptions.DeadLetterTopic = "amountDeadLetterTopic"; - var withdrawTopicOptions = new TopicOptions(); - withdrawTopicOptions.PubsubName = PubsubName; - withdrawTopicOptions.Name = "withdraw"; - withdrawTopicOptions.DeadLetterTopic = "amountDeadLetterTopic"; + var withdrawTopicOptions = new TopicOptions(); + withdrawTopicOptions.PubsubName = PubsubName; + withdrawTopicOptions.Name = "withdraw"; + withdrawTopicOptions.DeadLetterTopic = "amountDeadLetterTopic"; - var multiDepositTopicOptions = new TopicOptions { PubsubName = PubsubName, Name = "multideposit" }; - - var bulkSubscribeTopicOptions = new BulkSubscribeTopicOptions - { - TopicName = "multideposit", MaxMessagesCount = 250, MaxAwaitDurationMs = 1000 - }; + var multiDepositTopicOptions = new TopicOptions { PubsubName = PubsubName, Name = "multideposit" }; - endpoints.MapGet("{id}", Balance); - endpoints.MapPost("deposit", Deposit).WithTopic(depositTopicOptions); - endpoints.MapPost("multideposit", MultiDeposit).WithTopic(multiDepositTopicOptions).WithBulkSubscribe(bulkSubscribeTopicOptions); - endpoints.MapPost("deadLetterTopicRoute", ViewErrorMessage).WithTopic(PubsubName, "amountDeadLetterTopic"); - endpoints.MapPost("withdraw", Withdraw).WithTopic(withdrawTopicOptions); - }); - - async Task Balance(HttpContext context) + var bulkSubscribeTopicOptions = new BulkSubscribeTopicOptions { - logger.LogInformation("Enter Balance"); - var client = context.RequestServices.GetRequiredService(); + TopicName = "multideposit", MaxMessagesCount = 250, MaxAwaitDurationMs = 1000 + }; - var id = (string)context.Request.RouteValues["id"]; - logger.LogInformation("id is {0}", id); - var account = await client.GetStateAsync(StoreName, id); - if (account == null) - { - logger.LogInformation("Account not found"); - context.Response.StatusCode = 404; - return; - } + endpoints.MapGet("{id}", Balance); + endpoints.MapPost("deposit", Deposit).WithTopic(depositTopicOptions); + endpoints.MapPost("multideposit", MultiDeposit).WithTopic(multiDepositTopicOptions).WithBulkSubscribe(bulkSubscribeTopicOptions); + endpoints.MapPost("deadLetterTopicRoute", ViewErrorMessage).WithTopic(PubsubName, "amountDeadLetterTopic"); + endpoints.MapPost("withdraw", Withdraw).WithTopic(withdrawTopicOptions); + }); - logger.LogInformation("Account balance is {0}", account.Balance); + async Task Balance(HttpContext context) + { + logger.LogInformation("Enter Balance"); + var client = context.RequestServices.GetRequiredService(); - context.Response.ContentType = "application/json"; - await JsonSerializer.SerializeAsync(context.Response.Body, account, serializerOptions); + var id = (string)context.Request.RouteValues["id"]; + logger.LogInformation("id is {0}", id); + var account = await client.GetStateAsync(StoreName, id); + if (account == null) + { + logger.LogInformation("Account not found"); + context.Response.StatusCode = 404; + return; } - async Task Deposit(HttpContext context) - { - logger.LogInformation("Enter Deposit"); + logger.LogInformation("Account balance is {0}", account.Balance); - var client = context.RequestServices.GetRequiredService(); - var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); + context.Response.ContentType = "application/json"; + await JsonSerializer.SerializeAsync(context.Response.Body, account, serializerOptions); + } - logger.LogInformation("Id is {0}, Amount is {1}", transaction.Id, transaction.Amount); + async Task Deposit(HttpContext context) + { + logger.LogInformation("Enter Deposit"); - var account = await client.GetStateAsync(StoreName, transaction.Id); - if (account == null) - { - account = new Account() { Id = transaction.Id, }; - } + var client = context.RequestServices.GetRequiredService(); + var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); - if (transaction.Amount < 0m) - { - logger.LogInformation("Invalid amount"); - context.Response.StatusCode = 400; - return; - } + logger.LogInformation("Id is {0}, Amount is {1}", transaction.Id, transaction.Amount); - account.Balance += transaction.Amount; - await client.SaveStateAsync(StoreName, transaction.Id, account); - logger.LogInformation("Balance is {0}", account.Balance); + var account = await client.GetStateAsync(StoreName, transaction.Id); + if (account == null) + { + account = new Account() { Id = transaction.Id, }; + } - context.Response.ContentType = "application/json"; - await JsonSerializer.SerializeAsync(context.Response.Body, account, serializerOptions); + if (transaction.Amount < 0m) + { + logger.LogInformation("Invalid amount"); + context.Response.StatusCode = 400; + return; } + + account.Balance += transaction.Amount; + await client.SaveStateAsync(StoreName, transaction.Id, account); + logger.LogInformation("Balance is {0}", account.Balance); + + context.Response.ContentType = "application/json"; + await JsonSerializer.SerializeAsync(context.Response.Body, account, serializerOptions); + } - async Task MultiDeposit(HttpContext context) - { - logger.LogInformation("Enter bulk deposit"); + async Task MultiDeposit(HttpContext context) + { + logger.LogInformation("Enter bulk deposit"); - var client = context.RequestServices.GetRequiredService(); + var client = context.RequestServices.GetRequiredService(); - var bulkMessage = await JsonSerializer.DeserializeAsync>>( - context.Request.Body, serializerOptions); + var bulkMessage = await JsonSerializer.DeserializeAsync>>( + context.Request.Body, serializerOptions); - List entries = new List(); + List entries = new List(); - if (bulkMessage != null) + if (bulkMessage != null) + { + foreach (var entry in bulkMessage.Entries) { - foreach (var entry in bulkMessage.Entries) + try { - try - { - var transaction = entry.Event.Data; - - var state = await client.GetStateEntryAsync(StoreName, transaction.Id); - state.Value ??= new Account() { Id = transaction.Id, }; - logger.LogInformation("Id is {0}, the amount to be deposited is {1}", - transaction.Id, transaction.Amount); - - if (transaction.Amount < 0m) - { - logger.LogInformation("Invalid amount"); - context.Response.StatusCode = 400; - return; - } - - state.Value.Balance += transaction.Amount; - logger.LogInformation("Balance is {0}", state.Value.Balance); - await state.SaveAsync(); - entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, - BulkSubscribeAppResponseStatus.SUCCESS)); - } - catch (Exception e) + var transaction = entry.Event.Data; + + var state = await client.GetStateEntryAsync(StoreName, transaction.Id); + state.Value ??= new Account() { Id = transaction.Id, }; + logger.LogInformation("Id is {0}, the amount to be deposited is {1}", + transaction.Id, transaction.Amount); + + if (transaction.Amount < 0m) { - logger.LogError(e.Message); - entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, - BulkSubscribeAppResponseStatus.RETRY)); + logger.LogInformation("Invalid amount"); + context.Response.StatusCode = 400; + return; } + + state.Value.Balance += transaction.Amount; + logger.LogInformation("Balance is {0}", state.Value.Balance); + await state.SaveAsync(); + entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, + BulkSubscribeAppResponseStatus.SUCCESS)); + } + catch (Exception e) + { + logger.LogError(e.Message); + entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, + BulkSubscribeAppResponseStatus.RETRY)); } } - - await JsonSerializer.SerializeAsync(context.Response.Body, - new BulkSubscribeAppResponse(entries), serializerOptions); } - async Task ViewErrorMessage(HttpContext context) - { - var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); + await JsonSerializer.SerializeAsync(context.Response.Body, + new BulkSubscribeAppResponse(entries), serializerOptions); + } - logger.LogInformation("The amount cannot be negative: {0}", transaction.Amount); + async Task ViewErrorMessage(HttpContext context) + { + var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); - return; - } + logger.LogInformation("The amount cannot be negative: {0}", transaction.Amount); - async Task Withdraw(HttpContext context) - { - logger.LogInformation("Enter Withdraw"); + return; + } - var client = context.RequestServices.GetRequiredService(); - var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); + async Task Withdraw(HttpContext context) + { + logger.LogInformation("Enter Withdraw"); - logger.LogInformation("Id is {0}, Amount is {1}", transaction.Id, transaction.Amount); + var client = context.RequestServices.GetRequiredService(); + var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); - var account = await client.GetStateAsync(StoreName, transaction.Id); - if (account == null) - { - logger.LogInformation("Account not found"); - context.Response.StatusCode = 404; - return; - } + logger.LogInformation("Id is {0}, Amount is {1}", transaction.Id, transaction.Amount); - if (transaction.Amount < 0m) - { - logger.LogInformation("Invalid amount"); - context.Response.StatusCode = 400; - return; - } - - account.Balance -= transaction.Amount; - await client.SaveStateAsync(StoreName, transaction.Id, account); - logger.LogInformation("Balance is {0}", account.Balance); + var account = await client.GetStateAsync(StoreName, transaction.Id); + if (account == null) + { + logger.LogInformation("Account not found"); + context.Response.StatusCode = 404; + return; + } - context.Response.ContentType = "application/json"; - await JsonSerializer.SerializeAsync(context.Response.Body, account, serializerOptions); + if (transaction.Amount < 0m) + { + logger.LogInformation("Invalid amount"); + context.Response.StatusCode = 400; + return; } + + account.Balance -= transaction.Amount; + await client.SaveStateAsync(StoreName, transaction.Id, account); + logger.LogInformation("Balance is {0}", account.Balance); + + context.Response.ContentType = "application/json"; + await JsonSerializer.SerializeAsync(context.Response.Body, account, serializerOptions); } } -} +} \ No newline at end of file diff --git a/examples/AspNetCore/RoutingSample/Transaction.cs b/examples/AspNetCore/RoutingSample/Transaction.cs index f37340f45..b6042e433 100644 --- a/examples/AspNetCore/RoutingSample/Transaction.cs +++ b/examples/AspNetCore/RoutingSample/Transaction.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace RoutingSample +namespace RoutingSample; + +/// +/// Represents a transaction used by sample code. +/// +public class Transaction { /// - /// Represents a transaction used by sample code. + /// Gets or sets account id for the transaction. /// - public class Transaction - { - /// - /// Gets or sets account id for the transaction. - /// - public string Id { get; set; } + public string Id { get; set; } - /// - /// Gets or sets amount for the transaction. - /// - public decimal Amount { get; set; } - } + /// + /// Gets or sets amount for the transaction. + /// + public decimal Amount { get; set; } } \ No newline at end of file diff --git a/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs b/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs index 658d6d163..fd4b31c8d 100644 --- a/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs +++ b/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs @@ -11,64 +11,63 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace SecretStoreConfigurationProviderSample -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; - using Dapr.Client; - using Dapr.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using System; +namespace SecretStoreConfigurationProviderSample; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Dapr.Client; +using Dapr.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; +/// +/// Secret Store Configuration Provider Sample. +/// +public class Program +{ /// - /// Secret Store Configuration Provider Sample. + /// Main for Secret Store Configuration Provider Sample. /// - public class Program + /// Arguments. + public static void Main(string[] args) { - /// - /// Main for Secret Store Configuration Provider Sample. - /// - /// Arguments. - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } + CreateHostBuilder(args).Build().Run(); + } - /// - /// Creates WebHost Builder. - /// - /// Arguments. - /// Returns IHostbuilder. - public static IHostBuilder CreateHostBuilder(string[] args) - { - // Create Dapr Client - var client = new DaprClientBuilder() - .Build(); + /// + /// Creates WebHost Builder. + /// + /// Arguments. + /// Returns IHostbuilder. + public static IHostBuilder CreateHostBuilder(string[] args) + { + // Create Dapr Client + var client = new DaprClientBuilder() + .Build(); - return Host.CreateDefaultBuilder(args) - .ConfigureServices((services) => - { - // Add the DaprClient to DI. - services.AddSingleton(client); - }) - .ConfigureAppConfiguration((configBuilder) => - { - // To retrive specific secrets use secretDescriptors - // Create descriptors for the secrets you want to rerieve from the Dapr Secret Store. - // var secretDescriptors = new DaprSecretDescriptor[] - // { - // new DaprSecretDescriptor("super-secret") - // }; - // configBuilder.AddDaprSecretStore("demosecrets", secretDescriptors, client); + return Host.CreateDefaultBuilder(args) + .ConfigureServices((services) => + { + // Add the DaprClient to DI. + services.AddSingleton(client); + }) + .ConfigureAppConfiguration((configBuilder) => + { + // To retrive specific secrets use secretDescriptors + // Create descriptors for the secrets you want to rerieve from the Dapr Secret Store. + // var secretDescriptors = new DaprSecretDescriptor[] + // { + // new DaprSecretDescriptor("super-secret") + // }; + // configBuilder.AddDaprSecretStore("demosecrets", secretDescriptors, client); - // Add the secret store Configuration Provider to the configuration builder. - // Including a TimeSpan allows us to dictate how long we should wait for the Sidecar to start. - configBuilder.AddDaprSecretStore("demosecrets", client, TimeSpan.FromSeconds(10)); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } + // Add the secret store Configuration Provider to the configuration builder. + // Including a TimeSpan allows us to dictate how long we should wait for the Sidecar to start. + configBuilder.AddDaprSecretStore("demosecrets", client, TimeSpan.FromSeconds(10)); + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } } \ No newline at end of file diff --git a/examples/AspNetCore/SecretStoreConfigurationProviderSample/Startup.cs b/examples/AspNetCore/SecretStoreConfigurationProviderSample/Startup.cs index 6d2268d64..c89dcff82 100644 --- a/examples/AspNetCore/SecretStoreConfigurationProviderSample/Startup.cs +++ b/examples/AspNetCore/SecretStoreConfigurationProviderSample/Startup.cs @@ -11,72 +11,71 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace SecretStoreConfigurationProviderSample -{ - using System.Text.Json; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.DependencyInjection; +namespace SecretStoreConfigurationProviderSample; + +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +/// +/// Startup class. +/// +public class Startup +{ /// - /// Startup class. + /// Initializes a new instance of the class. /// - public class Startup + /// Configuration. + public Startup(IConfiguration configuration) { - /// - /// Initializes a new instance of the class. - /// - /// Configuration. - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } - /// - /// Gets the configuration. - /// - public IConfiguration Configuration { get; } + /// + /// Gets the configuration. + /// + public IConfiguration Configuration { get; } - /// - /// Configures Services. - /// - /// Service Collection. - public void ConfigureServices(IServiceCollection services) - { + /// + /// Configures Services. + /// + /// Service Collection. + public void ConfigureServices(IServiceCollection services) + { - } + } - /// - /// Configures Application Builder and WebHost environment. - /// - /// Application builder. - /// Webhost environment. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + /// + /// Configures Application Builder and WebHost environment. + /// + /// Application builder. + /// Webhost environment. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGet("secret", Secret); - }); + app.UseEndpoints(endpoints => + { + endpoints.MapGet("secret", Secret); + }); - async Task Secret(HttpContext context) - { - // Get secret from configuration!!! - var secretValue = Configuration["super-secret"]; + async Task Secret(HttpContext context) + { + // Get secret from configuration!!! + var secretValue = Configuration["super-secret"]; - context.Response.ContentType = "application/json"; - await JsonSerializer.SerializeAsync(context.Response.Body, secretValue); - } + context.Response.ContentType = "application/json"; + await JsonSerializer.SerializeAsync(context.Response.Body, secretValue); } } } \ No newline at end of file diff --git a/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs b/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs index 9ceb60c0b..faad24079 100644 --- a/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs +++ b/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs @@ -7,81 +7,80 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -namespace ConfigurationApi.Controllers +namespace ConfigurationApi.Controllers; + +[ApiController] +[Route("configuration")] +public class ConfigurationController : ControllerBase { - [ApiController] - [Route("configuration")] - public class ConfigurationController : ControllerBase + private ILogger logger; + private IConfiguration configuration; + private DaprClient client; + + public ConfigurationController(ILogger logger, IConfiguration configuration, [FromServices] DaprClient client) + { + this.logger = logger; + this.configuration = configuration; + this.client = client; + } + + [HttpGet("get/{configStore}/{queryKey}")] + public async Task GetConfiguration([FromRoute] string configStore, [FromRoute] string queryKey) { - private ILogger logger; - private IConfiguration configuration; - private DaprClient client; + logger.LogInformation($"Querying Configuration with key: {queryKey}"); + var configItems = await client.GetConfiguration(configStore, new List() { queryKey }); - public ConfigurationController(ILogger logger, IConfiguration configuration, [FromServices] DaprClient client) + if (configItems.Items.Count == 0) { - this.logger = logger; - this.configuration = configuration; - this.client = client; + logger.LogInformation($"No configuration item found for key: {queryKey}"); } - [HttpGet("get/{configStore}/{queryKey}")] - public async Task GetConfiguration([FromRoute] string configStore, [FromRoute] string queryKey) + foreach (var item in configItems.Items) { - logger.LogInformation($"Querying Configuration with key: {queryKey}"); - var configItems = await client.GetConfiguration(configStore, new List() { queryKey }); - - if (configItems.Items.Count == 0) - { - logger.LogInformation($"No configuration item found for key: {queryKey}"); - } - - foreach (var item in configItems.Items) - { - logger.LogInformation($"Got configuration item:\nKey: {item.Key}\nValue: {item.Value.Value}\nVersion: {item.Value.Version}"); - } + logger.LogInformation($"Got configuration item:\nKey: {item.Key}\nValue: {item.Value.Value}\nVersion: {item.Value.Version}"); } + } - [HttpGet("extension")] - public Task SubscribeAndWatchConfiguration() - { - logger.LogInformation($"Getting values from Configuration Extension, watched values ['withdrawVersion', 'source']."); + [HttpGet("extension")] + public Task SubscribeAndWatchConfiguration() + { + logger.LogInformation($"Getting values from Configuration Extension, watched values ['withdrawVersion', 'source']."); - logger.LogInformation($"'withdrawVersion' from extension: {configuration["withdrawVersion"]}"); - logger.LogInformation($"'source' from extension: {configuration["source"]}"); + logger.LogInformation($"'withdrawVersion' from extension: {configuration["withdrawVersion"]}"); + logger.LogInformation($"'source' from extension: {configuration["source"]}"); - return Task.CompletedTask; - } + return Task.CompletedTask; + } #nullable enable - [HttpPost("withdraw")] - public async Task> CreateAccountHandler(Transaction transaction) + [HttpPost("withdraw")] + public async Task> CreateAccountHandler(Transaction transaction) + { + // Check if the V2 method is enabled. + if (configuration["withdrawVersion"] == "v2") { - // Check if the V2 method is enabled. - if (configuration["withdrawVersion"] == "v2") + var source = !string.IsNullOrEmpty(configuration["source"]) ? configuration["source"] : "local"; + var transactionV2 = new TransactionV2 + { + Id = transaction.Id, + Amount = transaction.Amount, + Channel = source + }; + logger.LogInformation($"Calling V2 Withdraw API - Id: {transactionV2.Id} Amount: {transactionV2.Amount} Channel: {transactionV2.Channel}"); + try { - var source = !string.IsNullOrEmpty(configuration["source"]) ? configuration["source"] : "local"; - var transactionV2 = new TransactionV2 - { - Id = transaction.Id, - Amount = transaction.Amount, - Channel = source - }; - logger.LogInformation($"Calling V2 Withdraw API - Id: {transactionV2.Id} Amount: {transactionV2.Amount} Channel: {transactionV2.Channel}"); - try - { - return await this.client.InvokeMethodAsync("controller", "withdraw.v2", transactionV2); - } - catch (DaprException ex) - { - logger.LogError($"Error executing withdrawal: {ex.Message}"); - return BadRequest(); - } + return await this.client.InvokeMethodAsync("controller", "withdraw.v2", transactionV2); + } + catch (DaprException ex) + { + logger.LogError($"Error executing withdrawal: {ex.Message}"); + return BadRequest(); } - - // Default to the original method. - logger.LogInformation($"Calling V1 Withdraw API: {transaction}"); - return await this.client.InvokeMethodAsync("controller", "withdraw", transaction); } -#nullable disable + + // Default to the original method. + logger.LogInformation($"Calling V1 Withdraw API: {transaction}"); + return await this.client.InvokeMethodAsync("controller", "withdraw", transaction); } -} +#nullable disable +} \ No newline at end of file diff --git a/examples/Client/ConfigurationApi/Program.cs b/examples/Client/ConfigurationApi/Program.cs index 22f12cfae..832b06246 100644 --- a/examples/Client/ConfigurationApi/Program.cs +++ b/examples/Client/ConfigurationApi/Program.cs @@ -5,37 +5,36 @@ using Dapr.Extensions.Configuration; using System.Collections.Generic; -namespace ConfigurationApi +namespace ConfigurationApi; + +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - Console.WriteLine("Starting application."); - CreateHostBuilder(args).Build().Run(); - Console.WriteLine("Closing application."); - } + Console.WriteLine("Starting application."); + CreateHostBuilder(args).Build().Run(); + Console.WriteLine("Closing application."); + } - /// - /// Creates WebHost Builder. - /// - /// Arguments. - /// Returns IHostbuilder. - public static IHostBuilder CreateHostBuilder(string[] args) - { - var client = new DaprClientBuilder().Build(); - return Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(config => - { - // Get the initial value and continue to watch it for changes. - config.AddDaprConfigurationStore("redisconfig", new List() { "withdrawVersion" }, client, TimeSpan.FromSeconds(20)); - config.AddStreamingDaprConfigurationStore("redisconfig", new List() { "withdrawVersion", "source" }, client, TimeSpan.FromSeconds(20)); + /// + /// Creates WebHost Builder. + /// + /// Arguments. + /// Returns IHostbuilder. + public static IHostBuilder CreateHostBuilder(string[] args) + { + var client = new DaprClientBuilder().Build(); + return Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(config => + { + // Get the initial value and continue to watch it for changes. + config.AddDaprConfigurationStore("redisconfig", new List() { "withdrawVersion" }, client, TimeSpan.FromSeconds(20)); + config.AddStreamingDaprConfigurationStore("redisconfig", new List() { "withdrawVersion", "source" }, client, TimeSpan.FromSeconds(20)); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } -} +} \ No newline at end of file diff --git a/examples/Client/ConfigurationApi/Startup.cs b/examples/Client/ConfigurationApi/Startup.cs index b858b810c..6ab1f8cf8 100644 --- a/examples/Client/ConfigurationApi/Startup.cs +++ b/examples/Client/ConfigurationApi/Startup.cs @@ -4,51 +4,50 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace ConfigurationApi +namespace ConfigurationApi; + +public class Startup { - public class Startup + /// + /// Initializes a new instance of the class. + /// + /// Configuration. + public Startup(IConfiguration configuration) { - /// - /// Initializes a new instance of the class. - /// - /// Configuration. - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } - /// - /// Gets the configuration. - /// - public IConfiguration Configuration { get; } + /// + /// Gets the configuration. + /// + public IConfiguration Configuration { get; } - /// - /// Configures Services. - /// - /// Service Collection. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers().AddDapr(); - } + /// + /// Configures Services. + /// + /// Service Collection. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers().AddDapr(); + } - /// - /// Configures Application Builder and WebHost environment. - /// - /// Application builder. - /// Webhost environment. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + /// + /// Configures Application Builder and WebHost environment. + /// + /// Application builder. + /// Webhost environment. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); } -} +} \ No newline at end of file diff --git a/examples/Client/Cryptography/Example.cs b/examples/Client/Cryptography/Example.cs index 2c2d41626..ac3d525bb 100644 --- a/examples/Client/Cryptography/Example.cs +++ b/examples/Client/Cryptography/Example.cs @@ -11,12 +11,11 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Cryptography +namespace Cryptography; + +internal abstract class Example { - internal abstract class Example - { - public abstract string DisplayName { get; } + public abstract string DisplayName { get; } - public abstract Task RunAsync(CancellationToken cancellationToken); - } -} + public abstract Task RunAsync(CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs b/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs index 19df06345..43cf2dc9f 100644 --- a/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs +++ b/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs @@ -15,59 +15,58 @@ using Dapr.Client; #pragma warning disable CS0618 // Type or member is obsolete -namespace Cryptography.Examples +namespace Cryptography.Examples; + +internal class EncryptDecryptFileStreamExample(string componentName, string keyName) : Example { - internal class EncryptDecryptFileStreamExample(string componentName, string keyName) : Example + public override string DisplayName => "Use Cryptography to encrypt and decrypt a file"; + public override async Task RunAsync(CancellationToken cancellationToken) { - public override string DisplayName => "Use Cryptography to encrypt and decrypt a file"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + using var client = new DaprClientBuilder().Build(); - // The name of the file we're using as an example - const string fileName = "file.txt"; + // The name of the file we're using as an example + const string fileName = "file.txt"; - Console.WriteLine("Original file contents:"); - foreach (var line in await File.ReadAllLinesAsync(fileName, cancellationToken)) - { - Console.WriteLine(line); - } + Console.WriteLine("Original file contents:"); + foreach (var line in await File.ReadAllLinesAsync(fileName, cancellationToken)) + { + Console.WriteLine(line); + } - //Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer - await using var encryptFs = new FileStream(fileName, FileMode.Open); + //Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer + await using var encryptFs = new FileStream(fileName, FileMode.Open); - var bufferedEncryptedBytes = new ArrayBufferWriter(); - await foreach (var bytes in (await client.EncryptAsync(componentName, encryptFs, keyName, - new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken)) - .WithCancellation(cancellationToken)) - { - bufferedEncryptedBytes.Write(bytes.Span); - } + var bufferedEncryptedBytes = new ArrayBufferWriter(); + await foreach (var bytes in (await client.EncryptAsync(componentName, encryptFs, keyName, + new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken)) + .WithCancellation(cancellationToken)) + { + bufferedEncryptedBytes.Write(bytes.Span); + } - Console.WriteLine("Encrypted bytes:"); - Console.WriteLine(Convert.ToBase64String(bufferedEncryptedBytes.WrittenMemory.ToArray())); + Console.WriteLine("Encrypted bytes:"); + Console.WriteLine(Convert.ToBase64String(bufferedEncryptedBytes.WrittenMemory.ToArray())); - //We'll write to a temporary file via a FileStream - var tempDecryptedFile = Path.GetTempFileName(); - await using var decryptFs = new FileStream(tempDecryptedFile, FileMode.Create); + //We'll write to a temporary file via a FileStream + var tempDecryptedFile = Path.GetTempFileName(); + await using var decryptFs = new FileStream(tempDecryptedFile, FileMode.Create); - //We'll stream the decrypted bytes from a MemoryStream into the above temporary file - await using var encryptedMs = new MemoryStream(bufferedEncryptedBytes.WrittenMemory.ToArray()); - await foreach (var result in (await client.DecryptAsync(componentName, encryptedMs, keyName, - cancellationToken)).WithCancellation(cancellationToken)) - { - decryptFs.Write(result.Span); - } + //We'll stream the decrypted bytes from a MemoryStream into the above temporary file + await using var encryptedMs = new MemoryStream(bufferedEncryptedBytes.WrittenMemory.ToArray()); + await foreach (var result in (await client.DecryptAsync(componentName, encryptedMs, keyName, + cancellationToken)).WithCancellation(cancellationToken)) + { + decryptFs.Write(result.Span); + } - decryptFs.Close(); + decryptFs.Close(); - //Let's confirm the value as written to the file - var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken); - Console.WriteLine("Decrypted value: "); - Console.WriteLine(decryptedValue); + //Let's confirm the value as written to the file + var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken); + Console.WriteLine("Decrypted value: "); + Console.WriteLine(decryptedValue); - //And some cleanup to delete our temp file - File.Delete(tempDecryptedFile); - } + //And some cleanup to delete our temp file + File.Delete(tempDecryptedFile); } -} +} \ No newline at end of file diff --git a/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs b/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs index d29b24a60..1a31e9153 100644 --- a/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs +++ b/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs @@ -15,29 +15,28 @@ using Dapr.Client; #pragma warning disable CS0618 // Type or member is obsolete -namespace Cryptography.Examples +namespace Cryptography.Examples; + +internal class EncryptDecryptStringExample(string componentName, string keyName) : Example { - internal class EncryptDecryptStringExample(string componentName, string keyName) : Example - { - public override string DisplayName => "Using Cryptography to encrypt and decrypt a string"; + public override string DisplayName => "Using Cryptography to encrypt and decrypt a string"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - const string plaintextStr = "This is the value we're going to encrypt today"; - Console.WriteLine($"Original string value: '{plaintextStr}'"); + const string plaintextStr = "This is the value we're going to encrypt today"; + Console.WriteLine($"Original string value: '{plaintextStr}'"); - //Encrypt the string - var plaintextBytes = Encoding.UTF8.GetBytes(plaintextStr); - var encryptedBytesResult = await client.EncryptAsync(componentName, plaintextBytes, keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), - cancellationToken); + //Encrypt the string + var plaintextBytes = Encoding.UTF8.GetBytes(plaintextStr); + var encryptedBytesResult = await client.EncryptAsync(componentName, plaintextBytes, keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), + cancellationToken); - Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'"); + Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'"); - //Decrypt the string - var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, cancellationToken); - Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'"); - } + //Decrypt the string + var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, cancellationToken); + Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'"); } -} +} \ No newline at end of file diff --git a/examples/Client/Cryptography/Program.cs b/examples/Client/Cryptography/Program.cs index 5c63d7361..9fe3ec979 100644 --- a/examples/Client/Cryptography/Program.cs +++ b/examples/Client/Cryptography/Program.cs @@ -13,37 +13,36 @@ using Cryptography.Examples; -namespace Cryptography +namespace Cryptography; + +class Program { - class Program - { - private const string ComponentName = "localstorage"; - private const string KeyName = "rsa-private-key.pem"; //This should match the name of your generated key - this sample expects an RSA symmetrical key. + private const string ComponentName = "localstorage"; + private const string KeyName = "rsa-private-key.pem"; //This should match the name of your generated key - this sample expects an RSA symmetrical key. - private static readonly Example[] Examples = new Example[] - { - new EncryptDecryptStringExample(ComponentName, KeyName), - new EncryptDecryptFileStreamExample(ComponentName, KeyName) - }; + private static readonly Example[] Examples = new Example[] + { + new EncryptDecryptStringExample(ComponentName, KeyName), + new EncryptDecryptFileStreamExample(ComponentName, KeyName) + }; - static async Task Main(string[] args) + static async Task Main(string[] args) + { + if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) { - if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) - { - var cts = new CancellationTokenSource(); - Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); + var cts = new CancellationTokenSource(); + Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); - await Examples[index].RunAsync(cts.Token); - return 0; - } + await Examples[index].RunAsync(cts.Token); + return 0; + } - Console.WriteLine("Hello, please choose a sample to run by passing your selection's number into the arguments, e.g. 'dotnet run 0':"); - for (var i = 0; i < Examples.Length; i++) - { - Console.WriteLine($"{i}: {Examples[i].DisplayName}"); - } - Console.WriteLine(); - return 1; + Console.WriteLine("Hello, please choose a sample to run by passing your selection's number into the arguments, e.g. 'dotnet run 0':"); + for (var i = 0; i < Examples.Length; i++) + { + Console.WriteLine($"{i}: {Examples[i].DisplayName}"); } + Console.WriteLine(); + return 1; } -} +} \ No newline at end of file diff --git a/examples/Client/DistributedLock/Controllers/BindingController.cs b/examples/Client/DistributedLock/Controllers/BindingController.cs index aa4dd1f52..37fbe637b 100644 --- a/examples/Client/DistributedLock/Controllers/BindingController.cs +++ b/examples/Client/DistributedLock/Controllers/BindingController.cs @@ -8,100 +8,99 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -namespace DistributedLock.Controllers +namespace DistributedLock.Controllers; + +[ApiController] +public class BindingController : ControllerBase { - [ApiController] - public class BindingController : ControllerBase - { - private DaprClient client; - private ILogger logger; - private string appId; + private DaprClient client; + private ILogger logger; + private string appId; - public BindingController(DaprClient client, ILogger logger) - { - this.client = client; - this.logger = logger; - this.appId = Environment.GetEnvironmentVariable("APP_ID"); - } + public BindingController(DaprClient client, ILogger logger) + { + this.client = client; + this.logger = logger; + this.appId = Environment.GetEnvironmentVariable("APP_ID"); + } - [HttpPost("cronbinding")] - [Obsolete] - public async Task HandleBindingEvent() - { - logger.LogInformation($"Received binding event on {appId}, scanning for work."); + [HttpPost("cronbinding")] + [Obsolete] + public async Task HandleBindingEvent() + { + logger.LogInformation($"Received binding event on {appId}, scanning for work."); - var request = new BindingRequest("localstorage", "list"); - var result = client.InvokeBindingAsync(request); + var request = new BindingRequest("localstorage", "list"); + var result = client.InvokeBindingAsync(request); - var rawData = result.Result.Data.ToArray(); - var files = JsonSerializer.Deserialize(rawData); + var rawData = result.Result.Data.ToArray(); + var files = JsonSerializer.Deserialize(rawData); - if (files != null) + if (files != null) + { + foreach (var file in files.Select(file => file.Split("/").Last()).OrderBy(file => file)) { - foreach (var file in files.Select(file => file.Split("/").Last()).OrderBy(file => file)) - { - await AttemptToProcessFile(file); - } + await AttemptToProcessFile(file); } - - return Ok(); } + + return Ok(); + } - [Obsolete] - private async Task AttemptToProcessFile(string fileName) + [Obsolete] + private async Task AttemptToProcessFile(string fileName) + { + // Locks are Disposable and will automatically unlock at the end of a 'using' statement. + logger.LogInformation($"Attempting to lock: {fileName}"); + await using (var fileLock = await client.Lock("redislock", fileName, appId, 60)) { - // Locks are Disposable and will automatically unlock at the end of a 'using' statement. - logger.LogInformation($"Attempting to lock: {fileName}"); - await using (var fileLock = await client.Lock("redislock", fileName, appId, 60)) + if (fileLock.Success) { - if (fileLock.Success) - { - logger.LogInformation($"Successfully locked file: {fileName}"); + logger.LogInformation($"Successfully locked file: {fileName}"); - // Get the file after we've locked it, we're safe here because of the lock. - var fileState = await GetFile(fileName); + // Get the file after we've locked it, we're safe here because of the lock. + var fileState = await GetFile(fileName); - if (fileState == null) - { - logger.LogWarning($"File {fileName} has already been processed!"); - return; - } + if (fileState == null) + { + logger.LogWarning($"File {fileName} has already been processed!"); + return; + } - // "Analyze" the file before committing it to our remote storage. - fileState.Analysis = fileState.Number > 50 ? "High" : "Low"; + // "Analyze" the file before committing it to our remote storage. + fileState.Analysis = fileState.Number > 50 ? "High" : "Low"; - // Save it to remote storage. - await client.SaveStateAsync("redisstore", fileName, fileState); + // Save it to remote storage. + await client.SaveStateAsync("redisstore", fileName, fileState); - // Remove it from local storage. - var bindingDeleteRequest = new BindingRequest("localstorage", "delete"); - bindingDeleteRequest.Metadata["fileName"] = fileName; - await client.InvokeBindingAsync(bindingDeleteRequest); + // Remove it from local storage. + var bindingDeleteRequest = new BindingRequest("localstorage", "delete"); + bindingDeleteRequest.Metadata["fileName"] = fileName; + await client.InvokeBindingAsync(bindingDeleteRequest); - logger.LogInformation($"Done processing {fileName}"); - } - else - { - logger.LogWarning($"Failed to lock {fileName}."); - } + logger.LogInformation($"Done processing {fileName}"); + } + else + { + logger.LogWarning($"Failed to lock {fileName}."); } } + } - private async Task GetFile(string fileName) + private async Task GetFile(string fileName) + { + try { - try - { - var bindingGetRequest = new BindingRequest("localstorage", "get"); - bindingGetRequest.Metadata["fileName"] = fileName; + var bindingGetRequest = new BindingRequest("localstorage", "get"); + bindingGetRequest.Metadata["fileName"] = fileName; - var bindingResponse = await client.InvokeBindingAsync(bindingGetRequest); - return JsonSerializer.Deserialize(bindingResponse.Data.ToArray()); - } - catch (DaprException) - { - return null; - } + var bindingResponse = await client.InvokeBindingAsync(bindingGetRequest); + return JsonSerializer.Deserialize(bindingResponse.Data.ToArray()); } + catch (DaprException) + { + return null; + } } -} +} \ No newline at end of file diff --git a/examples/Client/DistributedLock/Model/StateData.cs b/examples/Client/DistributedLock/Model/StateData.cs index 0ad5d2fd9..f5eeeb29d 100644 --- a/examples/Client/DistributedLock/Model/StateData.cs +++ b/examples/Client/DistributedLock/Model/StateData.cs @@ -1,15 +1,13 @@ -namespace DistributedLock.Model -{ +namespace DistributedLock.Model; #nullable enable - public class StateData - { - public int Number { get; } - public string? Analysis { get; set; } +public class StateData +{ + public int Number { get; } + public string? Analysis { get; set; } - public StateData(int number, string? analysis = null) - { - Number = number; - Analysis = analysis; - } + public StateData(int number, string? analysis = null) + { + Number = number; + Analysis = analysis; } -} +} \ No newline at end of file diff --git a/examples/Client/DistributedLock/Program.cs b/examples/Client/DistributedLock/Program.cs index 3080b5b1e..830fb5dc0 100644 --- a/examples/Client/DistributedLock/Program.cs +++ b/examples/Client/DistributedLock/Program.cs @@ -2,22 +2,21 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -namespace DistributedLock +namespace DistributedLock; + +public class Program { - public class Program + static string DEFAULT_APP_PORT = "22222"; + public static void Main(string[] args) { - static string DEFAULT_APP_PORT = "22222"; - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup() - .UseUrls($"http://localhost:{Environment.GetEnvironmentVariable("APP_PORT") ?? DEFAULT_APP_PORT}"); - }); + CreateHostBuilder(args).Build().Run(); } -} + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup() + .UseUrls($"http://localhost:{Environment.GetEnvironmentVariable("APP_PORT") ?? DEFAULT_APP_PORT}"); + }); +} \ No newline at end of file diff --git a/examples/Client/DistributedLock/Services/GeneratorService.cs b/examples/Client/DistributedLock/Services/GeneratorService.cs index aa59d9b75..4d5c0a3ff 100644 --- a/examples/Client/DistributedLock/Services/GeneratorService.cs +++ b/examples/Client/DistributedLock/Services/GeneratorService.cs @@ -3,30 +3,29 @@ using Dapr.Client; using DistributedLock.Model; -namespace DistributedLock.Services +namespace DistributedLock.Services; + +public class GeneratorService { - public class GeneratorService - { - Timer generateDataTimer; + Timer generateDataTimer; - public GeneratorService() + public GeneratorService() + { + // Generate some data every second. + if (Environment.GetEnvironmentVariable("APP_ID") == "generator") { - // Generate some data every second. - if (Environment.GetEnvironmentVariable("APP_ID") == "generator") - { - generateDataTimer = new Timer(GenerateData, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10)); - } + generateDataTimer = new Timer(GenerateData, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10)); } + } - public async void GenerateData(Object stateInfo) + public async void GenerateData(Object stateInfo) + { + using (var client = new DaprClientBuilder().Build()) { - using (var client = new DaprClientBuilder().Build()) - { - var rand = new Random(); - var state = new StateData(rand.Next(100)); + var rand = new Random(); + var state = new StateData(rand.Next(100)); - await client.InvokeBindingAsync("localstorage", "create", state); - } + await client.InvokeBindingAsync("localstorage", "create", state); } } -} +} \ No newline at end of file diff --git a/examples/Client/DistributedLock/Startup.cs b/examples/Client/DistributedLock/Startup.cs index 9f40e4752..0e2e0df3a 100644 --- a/examples/Client/DistributedLock/Startup.cs +++ b/examples/Client/DistributedLock/Startup.cs @@ -1,46 +1,45 @@ using Dapr.AspNetCore; -using DistributedLock.Services; +using DistributedLock.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace DistributedLock +namespace DistributedLock; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers().AddDapr(); - services.AddLogging(); - services.AddSingleton(new GeneratorService()); - } + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers().AddDapr(); + services.AddLogging(); + services.AddSingleton(new GeneratorService()); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseAuthorization(); + app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); } -} +} \ No newline at end of file diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs index 343618457..90b5d9c68 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs @@ -17,46 +17,45 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class BulkPublishEventExample : Example { - public class BulkPublishEventExample : Example - { - private const string PubsubName = "pubsub"; - private const string TopicName = "deposit"; + private const string PubsubName = "pubsub"; + private const string TopicName = "deposit"; - IReadOnlyList BulkPublishData = new List() { - new { Id = "17", Amount = 10m }, - new { Id = "18", Amount = 20m }, - new { Id = "19", Amount = 30m } - }; + IReadOnlyList BulkPublishData = new List() { + new { Id = "17", Amount = 10m }, + new { Id = "18", Amount = 20m }, + new { Id = "19", Amount = 30m } + }; - public override string DisplayName => "Bulk Publishing Events"; + public override string DisplayName => "Bulk Publishing Events"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - var res = await client.BulkPublishEventAsync(PubsubName, TopicName, - BulkPublishData); + var res = await client.BulkPublishEventAsync(PubsubName, TopicName, + BulkPublishData); - if (res != null) { - if (res.FailedEntries.Count > 0) - { - Console.WriteLine("Some events failed to be published!"); + if (res != null) { + if (res.FailedEntries.Count > 0) + { + Console.WriteLine("Some events failed to be published!"); - foreach (var failedEntry in res.FailedEntries) - { - Console.WriteLine("EntryId : " + failedEntry.Entry.EntryId + " Error message : " + - failedEntry.ErrorMessage); - } - } - else + foreach (var failedEntry in res.FailedEntries) { - Console.WriteLine("Published multiple deposit events!"); + Console.WriteLine("EntryId : " + failedEntry.Entry.EntryId + " Error message : " + + failedEntry.ErrorMessage); } - } else { - throw new Exception("null response from dapr"); } + else + { + Console.WriteLine("Published multiple deposit events!"); + } + } else { + throw new Exception("null response from dapr"); } } -} +} \ No newline at end of file diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/Example.cs b/examples/Client/PublishSubscribe/BulkPublishEventExample/Example.cs index e12e38d16..eb0b8c3d9 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/Example.cs +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/Example.cs @@ -14,12 +14,11 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +public abstract class Example { - public abstract class Example - { - public abstract string DisplayName { get; } + public abstract string DisplayName { get; } - public abstract Task RunAsync(CancellationToken cancellationToken); - } -} + public abstract Task RunAsync(CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs b/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs index 476982353..b540c6189 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs @@ -15,33 +15,32 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +class Program { - class Program + private static readonly Example[] Examples = new Example[] { - private static readonly Example[] Examples = new Example[] - { - new BulkPublishEventExample(), - }; + new BulkPublishEventExample(), + }; - static async Task Main(string[] args) + static async Task Main(string[] args) + { + if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) { - if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) - { - var cts = new CancellationTokenSource(); - Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); - - await Examples[index].RunAsync(cts.Token); - return 0; - } + var cts = new CancellationTokenSource(); + Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); - Console.WriteLine("Hello, please choose a sample to run:"); - for (var i = 0; i < Examples.Length; i++) - { - Console.WriteLine($"{i}: {Examples[i].DisplayName}"); - } - Console.WriteLine(); + await Examples[index].RunAsync(cts.Token); return 0; } + + Console.WriteLine("Hello, please choose a sample to run:"); + for (var i = 0; i < Examples.Length; i++) + { + Console.WriteLine($"{i}: {Examples[i].DisplayName}"); + } + Console.WriteLine(); + return 0; } -} +} \ No newline at end of file diff --git a/examples/Client/PublishSubscribe/PublishEventExample/Example.cs b/examples/Client/PublishSubscribe/PublishEventExample/Example.cs index fbe78124c..fe51b56f2 100644 --- a/examples/Client/PublishSubscribe/PublishEventExample/Example.cs +++ b/examples/Client/PublishSubscribe/PublishEventExample/Example.cs @@ -14,14 +14,13 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +public abstract class Example { - public abstract class Example - { - protected static readonly string pubsubName = "pubsub"; + protected static readonly string pubsubName = "pubsub"; - public abstract string DisplayName { get; } + public abstract string DisplayName { get; } - public abstract Task RunAsync(CancellationToken cancellationToken); - } -} + public abstract Task RunAsync(CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/examples/Client/PublishSubscribe/PublishEventExample/Program.cs b/examples/Client/PublishSubscribe/PublishEventExample/Program.cs index af74ad906..e1f5c95ee 100644 --- a/examples/Client/PublishSubscribe/PublishEventExample/Program.cs +++ b/examples/Client/PublishSubscribe/PublishEventExample/Program.cs @@ -15,34 +15,33 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +class Program { - class Program + private static readonly Example[] Examples = new Example[] { - private static readonly Example[] Examples = new Example[] - { - new PublishEventExample(), - new PublishBytesExample(), - }; + new PublishEventExample(), + new PublishBytesExample(), + }; - static async Task Main(string[] args) + static async Task Main(string[] args) + { + if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) { - if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) - { - var cts = new CancellationTokenSource(); - Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); - - await Examples[index].RunAsync(cts.Token); - return 0; - } + var cts = new CancellationTokenSource(); + Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); - Console.WriteLine("Hello, please choose a sample to run:"); - for (var i = 0; i < Examples.Length; i++) - { - Console.WriteLine($"{i}: {Examples[i].DisplayName}"); - } - Console.WriteLine(); + await Examples[index].RunAsync(cts.Token); return 0; } + + Console.WriteLine("Hello, please choose a sample to run:"); + for (var i = 0; i < Examples.Length; i++) + { + Console.WriteLine($"{i}: {Examples[i].DisplayName}"); + } + Console.WriteLine(); + return 0; } -} +} \ No newline at end of file diff --git a/examples/Client/PublishSubscribe/PublishEventExample/PublishBytesExample.cs b/examples/Client/PublishSubscribe/PublishEventExample/PublishBytesExample.cs index 3334421c4..4834e80a8 100644 --- a/examples/Client/PublishSubscribe/PublishEventExample/PublishBytesExample.cs +++ b/examples/Client/PublishSubscribe/PublishEventExample/PublishBytesExample.cs @@ -19,21 +19,20 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class PublishBytesExample : Example { - public class PublishBytesExample : Example - { - public override string DisplayName => "Publish Bytes"; + public override string DisplayName => "Publish Bytes"; - public async override Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public async override Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - var transaction = new { Id = "17", Amount = 30m }; - var content = JsonSerializer.SerializeToUtf8Bytes(transaction); + var transaction = new { Id = "17", Amount = 30m }; + var content = JsonSerializer.SerializeToUtf8Bytes(transaction); - await client.PublishByteEventAsync(pubsubName, "deposit", content.AsMemory(), MediaTypeNames.Application.Json, new Dictionary { }, cancellationToken); - Console.WriteLine("Published deposit event!"); - } + await client.PublishByteEventAsync(pubsubName, "deposit", content.AsMemory(), MediaTypeNames.Application.Json, new Dictionary { }, cancellationToken); + Console.WriteLine("Published deposit event!"); } -} +} \ No newline at end of file diff --git a/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.cs b/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.cs index 9d34ae509..e8b1e80e6 100644 --- a/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.cs +++ b/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.cs @@ -16,19 +16,18 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class PublishEventExample : Example { - public class PublishEventExample : Example - { - public override string DisplayName => "Publishing Events"; + public override string DisplayName => "Publishing Events"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - var eventData = new { Id = "17", Amount = 10m, }; - await client.PublishEventAsync(pubsubName, "deposit", eventData, cancellationToken); - Console.WriteLine("Published deposit event!"); - } + var eventData = new { Id = "17", Amount = 10m, }; + await client.PublishEventAsync(pubsubName, "deposit", eventData, cancellationToken); + Console.WriteLine("Published deposit event!"); } -} +} \ No newline at end of file diff --git a/examples/Client/ServiceInvocation/Example.cs b/examples/Client/ServiceInvocation/Example.cs index d2c07bfeb..507bdcfd8 100644 --- a/examples/Client/ServiceInvocation/Example.cs +++ b/examples/Client/ServiceInvocation/Example.cs @@ -14,12 +14,11 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +public abstract class Example { - public abstract class Example - { - public abstract string DisplayName { get; } + public abstract string DisplayName { get; } - public abstract Task RunAsync(CancellationToken cancellationToken); - } -} + public abstract Task RunAsync(CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/examples/Client/ServiceInvocation/InvokeServiceGrpcExample.cs b/examples/Client/ServiceInvocation/InvokeServiceGrpcExample.cs index 564cf038d..127d90037 100644 --- a/examples/Client/ServiceInvocation/InvokeServiceGrpcExample.cs +++ b/examples/Client/ServiceInvocation/InvokeServiceGrpcExample.cs @@ -17,32 +17,31 @@ using Dapr.Client; using GrpcServiceSample.Generated; -namespace Samples.Client +namespace Samples.Client; + +public class InvokeServiceGrpcExample : Example { - public class InvokeServiceGrpcExample : Example - { - public override string DisplayName => "Invoking a gRPC service with gRPC semantics and Protobuf with DaprClient"; + public override string DisplayName => "Invoking a gRPC service with gRPC semantics and Protobuf with DaprClient"; - // Note: the data types used in this sample are generated from data.proto in GrpcServiceSample - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + // Note: the data types used in this sample are generated from data.proto in GrpcServiceSample + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - Console.WriteLine("Invoking grpc deposit"); - var deposit = new GrpcServiceSample.Generated.Transaction() { Id = "17", Amount = 99 }; - var account = await client.InvokeMethodGrpcAsync("grpcsample", "deposit", deposit, cancellationToken); - Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance); - Console.WriteLine("Completed grpc deposit"); + Console.WriteLine("Invoking grpc deposit"); + var deposit = new GrpcServiceSample.Generated.Transaction() { Id = "17", Amount = 99 }; + var account = await client.InvokeMethodGrpcAsync("grpcsample", "deposit", deposit, cancellationToken); + Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance); + Console.WriteLine("Completed grpc deposit"); - Console.WriteLine("Invoking grpc withdraw"); - var withdraw = new GrpcServiceSample.Generated.Transaction() { Id = "17", Amount = 10, }; - await client.InvokeMethodGrpcAsync("grpcsample", "withdraw", withdraw, cancellationToken); - Console.WriteLine("Completed grpc withdraw"); + Console.WriteLine("Invoking grpc withdraw"); + var withdraw = new GrpcServiceSample.Generated.Transaction() { Id = "17", Amount = 10, }; + await client.InvokeMethodGrpcAsync("grpcsample", "withdraw", withdraw, cancellationToken); + Console.WriteLine("Completed grpc withdraw"); - Console.WriteLine("Invoking grpc balance"); - var request = new GetAccountRequest() { Id = "17", }; - account = await client.InvokeMethodGrpcAsync("grpcsample", "getaccount", request, cancellationToken); - Console.WriteLine($"Received grpc balance {account.Balance}"); - } + Console.WriteLine("Invoking grpc balance"); + var request = new GetAccountRequest() { Id = "17", }; + account = await client.InvokeMethodGrpcAsync("grpcsample", "getaccount", request, cancellationToken); + Console.WriteLine($"Received grpc balance {account.Balance}"); } -} +} \ No newline at end of file diff --git a/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs b/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs index 72d68096d..415e984e7 100644 --- a/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs +++ b/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs @@ -17,41 +17,40 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class InvokeServiceHttpClientExample : Example { - public class InvokeServiceHttpClientExample : Example - { - public override string DisplayName => "Invoking an HTTP service with HttpClient"; + public override string DisplayName => "Invoking an HTTP service with HttpClient"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - var client = DaprClient.CreateInvokeHttpClient(appId: "routing"); + public override async Task RunAsync(CancellationToken cancellationToken) + { + var client = DaprClient.CreateInvokeHttpClient(appId: "routing"); - var deposit = new Transaction { Id = "17", Amount = 99m }; - var response = await client.PostAsJsonAsync("/deposit", deposit, cancellationToken); - var account = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); - Console.WriteLine("Returned: id:{0} | Balance:{1}", account?.Id, account?.Balance); + var deposit = new Transaction { Id = "17", Amount = 99m }; + var response = await client.PostAsJsonAsync("/deposit", deposit, cancellationToken); + var account = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); + Console.WriteLine("Returned: id:{0} | Balance:{1}", account?.Id, account?.Balance); - var withdraw = new Transaction { Id = "17", Amount = 10m, }; - response = await client.PostAsJsonAsync("/withdraw", withdraw, cancellationToken); - response.EnsureSuccessStatusCode(); + var withdraw = new Transaction { Id = "17", Amount = 10m, }; + response = await client.PostAsJsonAsync("/withdraw", withdraw, cancellationToken); + response.EnsureSuccessStatusCode(); - account = await client.GetFromJsonAsync("/17", cancellationToken); - Console.WriteLine($"Received balance {account?.Balance}"); - } + account = await client.GetFromJsonAsync("/17", cancellationToken); + Console.WriteLine($"Received balance {account?.Balance}"); + } - internal class Transaction - { - public string? Id { get; set; } + internal class Transaction + { + public string? Id { get; set; } - public decimal? Amount { get; set; } - } + public decimal? Amount { get; set; } + } - internal class Account - { - public string? Id { get; set; } + internal class Account + { + public string? Id { get; set; } - public decimal? Balance { get; set; } - } + public decimal? Balance { get; set; } } -} +} \ No newline at end of file diff --git a/examples/Client/ServiceInvocation/InvokeServiceHttpExample.cs b/examples/Client/ServiceInvocation/InvokeServiceHttpExample.cs index 74235c132..5595ae268 100644 --- a/examples/Client/ServiceInvocation/InvokeServiceHttpExample.cs +++ b/examples/Client/ServiceInvocation/InvokeServiceHttpExample.cs @@ -17,46 +17,45 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class InvokeServiceHttpExample : Example { - public class InvokeServiceHttpExample : Example + public override string DisplayName => "Invoking an HTTP service with DaprClient"; + + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); + + // Invokes a POST method named "deposit" that takes input of type "Transaction" as define in the RoutingSample. + Console.WriteLine("Invoking deposit"); + var data = new { id = "17", amount = 99m }; + var account = await client.InvokeMethodAsync("routing", "deposit", data, cancellationToken); + Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance); + + // Invokes a POST method named "Withdraw" that takes input of type "Transaction" as define in the RoutingSample. + Console.WriteLine("Invoking withdraw"); + data = new { id = "17", amount = 10m, }; + await client.InvokeMethodAsync("routing", "Withdraw", data, cancellationToken); + Console.WriteLine("Completed"); + + // Invokes a GET method named "hello" that takes input of type "MyData" and returns a string. + Console.WriteLine("Invoking balance"); + account = await client.InvokeMethodAsync(HttpMethod.Get, "routing", "17", cancellationToken); + Console.WriteLine($"Received balance {account.Balance}"); + } + + internal class Transaction { - public override string DisplayName => "Invoking an HTTP service with DaprClient"; - - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); - - // Invokes a POST method named "deposit" that takes input of type "Transaction" as define in the RoutingSample. - Console.WriteLine("Invoking deposit"); - var data = new { id = "17", amount = 99m }; - var account = await client.InvokeMethodAsync("routing", "deposit", data, cancellationToken); - Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance); - - // Invokes a POST method named "Withdraw" that takes input of type "Transaction" as define in the RoutingSample. - Console.WriteLine("Invoking withdraw"); - data = new { id = "17", amount = 10m, }; - await client.InvokeMethodAsync("routing", "Withdraw", data, cancellationToken); - Console.WriteLine("Completed"); - - // Invokes a GET method named "hello" that takes input of type "MyData" and returns a string. - Console.WriteLine("Invoking balance"); - account = await client.InvokeMethodAsync(HttpMethod.Get, "routing", "17", cancellationToken); - Console.WriteLine($"Received balance {account.Balance}"); - } - - internal class Transaction - { - public string? Id { get; set; } - - public decimal? Amount { get; set; } - } - - internal class Account - { - public string? Id { get; set; } - - public decimal? Balance { get; set; } - } + public string? Id { get; set; } + + public decimal? Amount { get; set; } + } + + internal class Account + { + public string? Id { get; set; } + + public decimal? Balance { get; set; } } -} +} \ No newline at end of file diff --git a/examples/Client/ServiceInvocation/Program.cs b/examples/Client/ServiceInvocation/Program.cs index b0c38ebcd..6d371337d 100644 --- a/examples/Client/ServiceInvocation/Program.cs +++ b/examples/Client/ServiceInvocation/Program.cs @@ -15,35 +15,34 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +class Program { - class Program + private static readonly Example[] Examples = new Example[] { - private static readonly Example[] Examples = new Example[] - { - new InvokeServiceGrpcExample(), - new InvokeServiceHttpExample(), - new InvokeServiceHttpClientExample(), - }; + new InvokeServiceGrpcExample(), + new InvokeServiceHttpExample(), + new InvokeServiceHttpClientExample(), + }; - static async Task Main(string[] args) + static async Task Main(string[] args) + { + if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) { - if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) - { - var cts = new CancellationTokenSource(); - Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); + var cts = new CancellationTokenSource(); + Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); - await Examples[index].RunAsync(cts.Token); - return 0; - } + await Examples[index].RunAsync(cts.Token); + return 0; + } - Console.WriteLine("Hello, please choose a sample to run:"); - for (var i = 0; i < Examples.Length; i++) - { - Console.WriteLine($"{i}: {Examples[i].DisplayName}"); - } - Console.WriteLine(); - return 1; + Console.WriteLine("Hello, please choose a sample to run:"); + for (var i = 0; i < Examples.Length; i++) + { + Console.WriteLine($"{i}: {Examples[i].DisplayName}"); } + Console.WriteLine(); + return 1; } -} +} \ No newline at end of file diff --git a/examples/Client/StateManagement/BulkStateExample.cs b/examples/Client/StateManagement/BulkStateExample.cs index 250c69cef..da25b157f 100644 --- a/examples/Client/StateManagement/BulkStateExample.cs +++ b/examples/Client/StateManagement/BulkStateExample.cs @@ -4,51 +4,50 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class BulkStateExample : Example { - public class BulkStateExample : Example - { - private static readonly string firstKey = "testKey1"; - private static readonly string secondKey = "testKey2"; - private static readonly string firstEtag = "123"; - private static readonly string secondEtag = "456"; - private static readonly string storeName = "statestore"; + private static readonly string firstKey = "testKey1"; + private static readonly string secondKey = "testKey2"; + private static readonly string firstEtag = "123"; + private static readonly string secondEtag = "456"; + private static readonly string storeName = "statestore"; - public override string DisplayName => "Using the State Store"; + public override string DisplayName => "Using the State Store"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - var state1 = new Widget() { Size = "small", Color = "yellow", }; - var state2 = new Widget() { Size = "big", Color = "green", }; + var state1 = new Widget() { Size = "small", Color = "yellow", }; + var state2 = new Widget() { Size = "big", Color = "green", }; - var stateItem1 = new SaveStateItem(firstKey, state1, firstEtag); - var stateItem2 = new SaveStateItem(secondKey, state2, secondEtag); + var stateItem1 = new SaveStateItem(firstKey, state1, firstEtag); + var stateItem2 = new SaveStateItem(secondKey, state2, secondEtag); - await client.SaveBulkStateAsync(storeName, new List>() { stateItem1, stateItem2}); + await client.SaveBulkStateAsync(storeName, new List>() { stateItem1, stateItem2}); - Console.WriteLine("Saved 2 States!"); + Console.WriteLine("Saved 2 States!"); - await Task.Delay(2000); + await Task.Delay(2000); - IReadOnlyList states = await client.GetBulkStateAsync(storeName, - new List(){firstKey, secondKey}, null); + IReadOnlyList states = await client.GetBulkStateAsync(storeName, + new List(){firstKey, secondKey}, null); - Console.WriteLine($"Got {states.Count} States: "); + Console.WriteLine($"Got {states.Count} States: "); - var deleteBulkStateItem1 = new BulkDeleteStateItem(states[0].Key, states[0].ETag); - var deleteBulkStateItem2 = new BulkDeleteStateItem(states[1].Key, states[1].ETag); + var deleteBulkStateItem1 = new BulkDeleteStateItem(states[0].Key, states[0].ETag); + var deleteBulkStateItem2 = new BulkDeleteStateItem(states[1].Key, states[1].ETag); - await client.DeleteBulkStateAsync(storeName, new List() { deleteBulkStateItem1, deleteBulkStateItem2 }); + await client.DeleteBulkStateAsync(storeName, new List() { deleteBulkStateItem1, deleteBulkStateItem2 }); - Console.WriteLine("Deleted States!"); - } + Console.WriteLine("Deleted States!"); + } - private class Widget - { - public string? Size { get; set; } - public string? Color { get; set; } - } + private class Widget + { + public string? Size { get; set; } + public string? Color { get; set; } } -} +} \ No newline at end of file diff --git a/examples/Client/StateManagement/Example.cs b/examples/Client/StateManagement/Example.cs index d2c07bfeb..507bdcfd8 100644 --- a/examples/Client/StateManagement/Example.cs +++ b/examples/Client/StateManagement/Example.cs @@ -14,12 +14,11 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +public abstract class Example { - public abstract class Example - { - public abstract string DisplayName { get; } + public abstract string DisplayName { get; } - public abstract Task RunAsync(CancellationToken cancellationToken); - } -} + public abstract Task RunAsync(CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/examples/Client/StateManagement/Program.cs b/examples/Client/StateManagement/Program.cs index e9ef36979..006a0224c 100644 --- a/examples/Client/StateManagement/Program.cs +++ b/examples/Client/StateManagement/Program.cs @@ -15,37 +15,36 @@ using System.Threading; using System.Threading.Tasks; -namespace Samples.Client +namespace Samples.Client; + +class Program { - class Program + private static readonly Example[] Examples = new Example[] { - private static readonly Example[] Examples = new Example[] - { - new StateStoreExample(), - new StateStoreTransactionsExample(), - new StateStoreETagsExample(), - new BulkStateExample(), - new StateStoreBinaryExample() - }; + new StateStoreExample(), + new StateStoreTransactionsExample(), + new StateStoreETagsExample(), + new BulkStateExample(), + new StateStoreBinaryExample() + }; - static async Task Main(string[] args) + static async Task Main(string[] args) + { + if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) { - if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) - { - var cts = new CancellationTokenSource(); - Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); + var cts = new CancellationTokenSource(); + Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); - await Examples[index].RunAsync(cts.Token); - return 0; - } + await Examples[index].RunAsync(cts.Token); + return 0; + } - Console.WriteLine("Hello, please choose a sample to run:"); - for (var i = 0; i < Examples.Length; i++) - { - Console.WriteLine($"{i}: {Examples[i].DisplayName}"); - } - Console.WriteLine(); - return 1; + Console.WriteLine("Hello, please choose a sample to run:"); + for (var i = 0; i < Examples.Length; i++) + { + Console.WriteLine($"{i}: {Examples[i].DisplayName}"); } + Console.WriteLine(); + return 1; } -} +} \ No newline at end of file diff --git a/examples/Client/StateManagement/StateStoreBinaryExample.cs b/examples/Client/StateManagement/StateStoreBinaryExample.cs index edf23704e..d29797c85 100644 --- a/examples/Client/StateManagement/StateStoreBinaryExample.cs +++ b/examples/Client/StateManagement/StateStoreBinaryExample.cs @@ -6,42 +6,41 @@ using System.Threading; using Google.Protobuf; -namespace Samples.Client +namespace Samples.Client; + +public class StateStoreBinaryExample : Example { - public class StateStoreBinaryExample : Example - { - private static readonly string stateKeyName = "binarydata"; - private static readonly string storeName = "statestore"; + private static readonly string stateKeyName = "binarydata"; + private static readonly string storeName = "statestore"; - public override string DisplayName => "Using the State Store with binary data"; + public override string DisplayName => "Using the State Store with binary data"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - var state = "Test Binary Data"; - // convert variable in to byte array - var stateBytes = Encoding.UTF8.GetBytes(state); - await client.SaveByteStateAsync(storeName, stateKeyName, stateBytes.AsMemory(), cancellationToken: cancellationToken); - Console.WriteLine("Saved State!"); + var state = "Test Binary Data"; + // convert variable in to byte array + var stateBytes = Encoding.UTF8.GetBytes(state); + await client.SaveByteStateAsync(storeName, stateKeyName, stateBytes.AsMemory(), cancellationToken: cancellationToken); + Console.WriteLine("Saved State!"); - var responseBytes = await client.GetByteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); - var savedState = Encoding.UTF8.GetString(ByteString.CopyFrom(responseBytes.Span).ToByteArray()); + var responseBytes = await client.GetByteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); + var savedState = Encoding.UTF8.GetString(ByteString.CopyFrom(responseBytes.Span).ToByteArray()); - if (savedState == null) - { - Console.WriteLine("State not found in store"); - } - else - { - Console.WriteLine($"Got State: {savedState}"); - } - - await client.DeleteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); - Console.WriteLine("Deleted State!"); + if (savedState == null) + { + Console.WriteLine("State not found in store"); + } + else + { + Console.WriteLine($"Got State: {savedState}"); } - + await client.DeleteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); + Console.WriteLine("Deleted State!"); } -} + + +} \ No newline at end of file diff --git a/examples/Client/StateManagement/StateStoreETagsExample.cs b/examples/Client/StateManagement/StateStoreETagsExample.cs index d8302253e..3f4e610af 100644 --- a/examples/Client/StateManagement/StateStoreETagsExample.cs +++ b/examples/Client/StateManagement/StateStoreETagsExample.cs @@ -16,62 +16,61 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class StateStoreETagsExample : Example { - public class StateStoreETagsExample : Example - { - private static readonly string stateKeyName = "widget"; - private static readonly string storeName = "statestore"; + private static readonly string stateKeyName = "widget"; + private static readonly string storeName = "statestore"; - public override string DisplayName => "Using the State Store with ETags"; + public override string DisplayName => "Using the State Store with ETags"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - // Save state which will create a new etag - await client.SaveStateAsync(storeName, stateKeyName, new Widget() { Size = "small", Color = "yellow", }, cancellationToken: cancellationToken); - Console.WriteLine($"Saved state which created a new entry with an initial etag"); + // Save state which will create a new etag + await client.SaveStateAsync(storeName, stateKeyName, new Widget() { Size = "small", Color = "yellow", }, cancellationToken: cancellationToken); + Console.WriteLine($"Saved state which created a new entry with an initial etag"); - // Read the state and etag - var (original, originalETag) = await client.GetStateAndETagAsync(storeName, stateKeyName, cancellationToken: cancellationToken); - Console.WriteLine($"Retrieved state: {original.Size} {original.Color} with etag: {originalETag}"); + // Read the state and etag + var (original, originalETag) = await client.GetStateAndETagAsync(storeName, stateKeyName, cancellationToken: cancellationToken); + Console.WriteLine($"Retrieved state: {original.Size} {original.Color} with etag: {originalETag}"); - // Save state which will update the etag - await client.SaveStateAsync(storeName, stateKeyName, new Widget() { Size = "small", Color = "yellow", }, cancellationToken: cancellationToken); - Console.WriteLine($"Saved state with new etag"); + // Save state which will update the etag + await client.SaveStateAsync(storeName, stateKeyName, new Widget() { Size = "small", Color = "yellow", }, cancellationToken: cancellationToken); + Console.WriteLine($"Saved state with new etag"); - // Modify the state and try saving it with the old etag. This will fail - original.Color = "purple"; - var isSaveStateSuccess = await client.TrySaveStateAsync(storeName, stateKeyName, original, originalETag, cancellationToken: cancellationToken); - Console.WriteLine($"Saved state with old etag: {originalETag} successfully? {isSaveStateSuccess}"); + // Modify the state and try saving it with the old etag. This will fail + original.Color = "purple"; + var isSaveStateSuccess = await client.TrySaveStateAsync(storeName, stateKeyName, original, originalETag, cancellationToken: cancellationToken); + Console.WriteLine($"Saved state with old etag: {originalETag} successfully? {isSaveStateSuccess}"); - // Read the updated state and etag - var (updated, updatedETag) = await client.GetStateAndETagAsync(storeName, stateKeyName, cancellationToken: cancellationToken); - Console.WriteLine($"Retrieved state: {updated.Size} {updated.Color} with etag: {updatedETag}"); + // Read the updated state and etag + var (updated, updatedETag) = await client.GetStateAndETagAsync(storeName, stateKeyName, cancellationToken: cancellationToken); + Console.WriteLine($"Retrieved state: {updated.Size} {updated.Color} with etag: {updatedETag}"); - // Modify the state and try saving it with the updated etag. This will succeed - updated.Color = "green"; - isSaveStateSuccess = await client.TrySaveStateAsync(storeName, stateKeyName, updated, updatedETag, cancellationToken: cancellationToken); - Console.WriteLine($"Saved state with latest etag: {updatedETag} successfully? {isSaveStateSuccess}"); + // Modify the state and try saving it with the updated etag. This will succeed + updated.Color = "green"; + isSaveStateSuccess = await client.TrySaveStateAsync(storeName, stateKeyName, updated, updatedETag, cancellationToken: cancellationToken); + Console.WriteLine($"Saved state with latest etag: {updatedETag} successfully? {isSaveStateSuccess}"); - // Delete with an old etag. This will fail - var isDeleteStateSuccess = await client.TryDeleteStateAsync(storeName, stateKeyName, originalETag, cancellationToken: cancellationToken); - Console.WriteLine($"Deleted state with old etag: {originalETag} successfully? {isDeleteStateSuccess}"); + // Delete with an old etag. This will fail + var isDeleteStateSuccess = await client.TryDeleteStateAsync(storeName, stateKeyName, originalETag, cancellationToken: cancellationToken); + Console.WriteLine($"Deleted state with old etag: {originalETag} successfully? {isDeleteStateSuccess}"); - // Read the updated state and etag - (updated, updatedETag) = await client.GetStateAndETagAsync(storeName, stateKeyName, cancellationToken: cancellationToken); - Console.WriteLine($"Retrieved state: {updated.Size} {updated.Color} with etag: {updatedETag}"); + // Read the updated state and etag + (updated, updatedETag) = await client.GetStateAndETagAsync(storeName, stateKeyName, cancellationToken: cancellationToken); + Console.WriteLine($"Retrieved state: {updated.Size} {updated.Color} with etag: {updatedETag}"); - // Delete with updated etag. This will succeed - isDeleteStateSuccess = await client.TryDeleteStateAsync(storeName, stateKeyName, updatedETag, cancellationToken: cancellationToken); - Console.WriteLine($"Deleted state with updated etag: {updatedETag} successfully? {isDeleteStateSuccess}"); - } + // Delete with updated etag. This will succeed + isDeleteStateSuccess = await client.TryDeleteStateAsync(storeName, stateKeyName, updatedETag, cancellationToken: cancellationToken); + Console.WriteLine($"Deleted state with updated etag: {updatedETag} successfully? {isDeleteStateSuccess}"); + } - private class Widget - { - public string? Size { get; set; } - public string? Color { get; set; } - } + private class Widget + { + public string? Size { get; set; } + public string? Color { get; set; } } -} +} \ No newline at end of file diff --git a/examples/Client/StateManagement/StateStoreExample.cs b/examples/Client/StateManagement/StateStoreExample.cs index 7dd53b1e8..f6eb43f03 100644 --- a/examples/Client/StateManagement/StateStoreExample.cs +++ b/examples/Client/StateManagement/StateStoreExample.cs @@ -16,41 +16,40 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class StateStoreExample : Example { - public class StateStoreExample : Example + private static readonly string stateKeyName = "widget"; + private static readonly string storeName = "statestore"; + + public override string DisplayName => "Using the State Store"; + + public override async Task RunAsync(CancellationToken cancellationToken) { - private static readonly string stateKeyName = "widget"; - private static readonly string storeName = "statestore"; + using var client = new DaprClientBuilder().Build(); - public override string DisplayName => "Using the State Store"; + var state = new Widget() { Size = "small", Color = "yellow", }; + await client.SaveStateAsync(storeName, stateKeyName, state, cancellationToken: cancellationToken); + Console.WriteLine("Saved State!"); - public override async Task RunAsync(CancellationToken cancellationToken) + state = await client.GetStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); + if (state == null) { - using var client = new DaprClientBuilder().Build(); - - var state = new Widget() { Size = "small", Color = "yellow", }; - await client.SaveStateAsync(storeName, stateKeyName, state, cancellationToken: cancellationToken); - Console.WriteLine("Saved State!"); - - state = await client.GetStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); - if (state == null) - { - Console.WriteLine("State not found in store"); - } - else - { - Console.WriteLine($"Got State: {state.Size} {state.Color}"); - } - - await client.DeleteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); - Console.WriteLine("Deleted State!"); + Console.WriteLine("State not found in store"); } - - private class Widget + else { - public string? Size { get; set; } - public string? Color { get; set; } + Console.WriteLine($"Got State: {state.Size} {state.Color}"); } + + await client.DeleteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); + Console.WriteLine("Deleted State!"); + } + + private class Widget + { + public string? Size { get; set; } + public string? Color { get; set; } } -} +} \ No newline at end of file diff --git a/examples/Client/StateManagement/StateStoreTransactionsExample.cs b/examples/Client/StateManagement/StateStoreTransactionsExample.cs index d3595876c..1989daf91 100644 --- a/examples/Client/StateManagement/StateStoreTransactionsExample.cs +++ b/examples/Client/StateManagement/StateStoreTransactionsExample.cs @@ -18,48 +18,47 @@ using System.Threading.Tasks; using Dapr.Client; -namespace Samples.Client +namespace Samples.Client; + +public class StateStoreTransactionsExample : Example { - public class StateStoreTransactionsExample : Example - { - private static readonly string storeName = "statestore"; + private static readonly string storeName = "statestore"; - public override string DisplayName => "Using the State Store with Transactions"; + public override string DisplayName => "Using the State Store with Transactions"; - public override async Task RunAsync(CancellationToken cancellationToken) - { - using var client = new DaprClientBuilder().Build(); + public override async Task RunAsync(CancellationToken cancellationToken) + { + using var client = new DaprClientBuilder().Build(); - var value = new Widget() { Size = "small", Color = "yellow", }; + var value = new Widget() { Size = "small", Color = "yellow", }; - // State transactions operate on raw bytes - var bytes = JsonSerializer.SerializeToUtf8Bytes(value); + // State transactions operate on raw bytes + var bytes = JsonSerializer.SerializeToUtf8Bytes(value); - var requests = new List() - { - new StateTransactionRequest("widget", bytes, StateOperationType.Upsert), - new StateTransactionRequest("oldwidget", null, StateOperationType.Delete) - }; + var requests = new List() + { + new StateTransactionRequest("widget", bytes, StateOperationType.Upsert), + new StateTransactionRequest("oldwidget", null, StateOperationType.Delete) + }; - Console.WriteLine("Executing transaction - save state and delete state"); - await client.ExecuteStateTransactionAsync(storeName, requests, cancellationToken: cancellationToken); - Console.WriteLine("Executed State Transaction!"); + Console.WriteLine("Executing transaction - save state and delete state"); + await client.ExecuteStateTransactionAsync(storeName, requests, cancellationToken: cancellationToken); + Console.WriteLine("Executed State Transaction!"); - var state = await client.GetStateAsync(storeName, "widget", cancellationToken: cancellationToken); - if (state == null) - { - Console.WriteLine("State not found in store"); - } - else - { - Console.WriteLine($"Got State: {state.Size} {state.Color}"); - } + var state = await client.GetStateAsync(storeName, "widget", cancellationToken: cancellationToken); + if (state == null) + { + Console.WriteLine("State not found in store"); } - - private class Widget + else { - public string? Size { get; set; } - public string? Color { get; set; } + Console.WriteLine($"Got State: {state.Size} {state.Color}"); } } -} + + private class Widget + { + public string? Size { get; set; } + public string? Color { get; set; } + } +} \ No newline at end of file diff --git a/examples/GeneratedActor/ActorClient/IGenericClientActor.cs b/examples/GeneratedActor/ActorClient/IGenericClientActor.cs index 166f4a9ef..33f500f8d 100644 --- a/examples/GeneratedActor/ActorClient/IGenericClientActor.cs +++ b/examples/GeneratedActor/ActorClient/IGenericClientActor.cs @@ -13,15 +13,14 @@ using Dapr.Actors.Generators; -namespace GeneratedActor +namespace GeneratedActor; + +[GenerateActorClient] +internal interface IGenericClientActor { - [GenerateActorClient] - internal interface IGenericClientActor - { - [ActorMethod(Name = "GetState")] - Task GetStateAsync(CancellationToken cancellationToken = default); + [ActorMethod(Name = "GetState")] + Task GetStateAsync(CancellationToken cancellationToken = default); - [ActorMethod(Name = "SetState")] - Task SetStateAsync(TGenericType2 state, CancellationToken cancellationToken = default); - } -} + [ActorMethod(Name = "SetState")] + Task SetStateAsync(TGenericType2 state, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/NotifyActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/NotifyActivity.cs index 7c4616bd0..c6a5a4180 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/NotifyActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/NotifyActivity.cs @@ -1,24 +1,23 @@ using Dapr.Workflow; using Microsoft.Extensions.Logging; -namespace WorkflowConsoleApp.Activities +namespace WorkflowConsoleApp.Activities; + +public record Notification(string Message); + +public class NotifyActivity : WorkflowActivity { - public record Notification(string Message); + readonly ILogger logger; - public class NotifyActivity : WorkflowActivity + public NotifyActivity(ILoggerFactory loggerFactory) { - readonly ILogger logger; - - public NotifyActivity(ILoggerFactory loggerFactory) - { - this.logger = loggerFactory.CreateLogger(); - } + this.logger = loggerFactory.CreateLogger(); + } - public override Task RunAsync(WorkflowActivityContext context, Notification notification) - { - this.logger.LogInformation(notification.Message); + public override Task RunAsync(WorkflowActivityContext context, Notification notification) + { + this.logger.LogInformation(notification.Message); - return Task.FromResult(null); - } + return Task.FromResult(null); } -} +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs index 4ca39f0ae..7138797d1 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs @@ -1,34 +1,33 @@ using Dapr.Workflow; using Microsoft.Extensions.Logging; -namespace WorkflowConsoleApp.Activities +namespace WorkflowConsoleApp.Activities; + +public class ProcessPaymentActivity : WorkflowActivity { - public class ProcessPaymentActivity : WorkflowActivity - { - readonly ILogger logger; + readonly ILogger logger; - public ProcessPaymentActivity(ILoggerFactory loggerFactory) - { - this.logger = loggerFactory.CreateLogger(); - } + public ProcessPaymentActivity(ILoggerFactory loggerFactory) + { + this.logger = loggerFactory.CreateLogger(); + } - public override async Task RunAsync(WorkflowActivityContext context, PaymentRequest req) - { - this.logger.LogInformation( - "Processing payment: {requestId} for {amount} {item} at ${currency}", - req.RequestId, - req.Amount, - req.ItemName, - req.Currency); + public override async Task RunAsync(WorkflowActivityContext context, PaymentRequest req) + { + this.logger.LogInformation( + "Processing payment: {requestId} for {amount} {item} at ${currency}", + req.RequestId, + req.Amount, + req.ItemName, + req.Currency); - // Simulate slow processing - await Task.Delay(TimeSpan.FromSeconds(7)); + // Simulate slow processing + await Task.Delay(TimeSpan.FromSeconds(7)); - this.logger.LogInformation( - "Payment for request ID '{requestId}' processed successfully", - req.RequestId); + this.logger.LogInformation( + "Payment for request ID '{requestId}' processed successfully", + req.RequestId); - return null; - } + return null; } -} +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs index d40078fc8..44e153c30 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs @@ -1,23 +1,22 @@ using Dapr.Workflow; using Microsoft.Extensions.Logging; -namespace WorkflowConsoleApp.Activities +namespace WorkflowConsoleApp.Activities; + +public class RequestApprovalActivity : WorkflowActivity { - public class RequestApprovalActivity : WorkflowActivity - { - readonly ILogger logger; + readonly ILogger logger; - public RequestApprovalActivity(ILoggerFactory loggerFactory) - { - this.logger = loggerFactory.CreateLogger(); - } + public RequestApprovalActivity(ILoggerFactory loggerFactory) + { + this.logger = loggerFactory.CreateLogger(); + } - public override Task RunAsync(WorkflowActivityContext context, OrderPayload input) - { - string orderId = context.InstanceId.ToString(); - this.logger.LogInformation("Requesting approval for order {orderId}", orderId); + public override Task RunAsync(WorkflowActivityContext context, OrderPayload input) + { + string orderId = context.InstanceId.ToString(); + this.logger.LogInformation("Requesting approval for order {orderId}", orderId); - return Task.FromResult(null); - } + return Task.FromResult(null); } -} +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs index cdae1c6ed..55dc1aaa8 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs @@ -2,57 +2,56 @@ using Dapr.Workflow; using Microsoft.Extensions.Logging; -namespace WorkflowConsoleApp.Activities +namespace WorkflowConsoleApp.Activities; + +public class ReserveInventoryActivity : WorkflowActivity { - public class ReserveInventoryActivity : WorkflowActivity + readonly ILogger logger; + readonly DaprClient client; + static readonly string storeName = "statestore"; + + public ReserveInventoryActivity(ILoggerFactory loggerFactory, DaprClient client) { - readonly ILogger logger; - readonly DaprClient client; - static readonly string storeName = "statestore"; + this.logger = loggerFactory.CreateLogger(); + this.client = client; + } - public ReserveInventoryActivity(ILoggerFactory loggerFactory, DaprClient client) + public override async Task RunAsync(WorkflowActivityContext context, InventoryRequest req) + { + this.logger.LogInformation( + "Reserving inventory for order '{requestId}' of {quantity} {name}", + req.RequestId, + req.Quantity, + req.ItemName); + + // Ensure that the store has items + InventoryItem item = await this.client.GetStateAsync( + storeName, + req.ItemName.ToLowerInvariant()); + + // Catch for the case where the statestore isn't setup + if (item == null) { - this.logger = loggerFactory.CreateLogger(); - this.client = client; + // Not enough items. + return new InventoryResult(false, item); } - public override async Task RunAsync(WorkflowActivityContext context, InventoryRequest req) - { - this.logger.LogInformation( - "Reserving inventory for order '{requestId}' of {quantity} {name}", - req.RequestId, - req.Quantity, - req.ItemName); - - // Ensure that the store has items - InventoryItem item = await this.client.GetStateAsync( - storeName, - req.ItemName.ToLowerInvariant()); - - // Catch for the case where the statestore isn't setup - if (item == null) - { - // Not enough items. - return new InventoryResult(false, item); - } - - this.logger.LogInformation( - "There are {quantity} {name} available for purchase", - item.Quantity, - item.Name); - - // See if there're enough items to purchase - if (item.Quantity >= req.Quantity) - { - // Simulate slow processing - await Task.Delay(TimeSpan.FromSeconds(2)); - - return new InventoryResult(true, item); - } + this.logger.LogInformation( + "There are {quantity} {name} available for purchase", + item.Quantity, + item.Name); - // Not enough items. - return new InventoryResult(false, item); + // See if there're enough items to purchase + if (item.Quantity >= req.Quantity) + { + // Simulate slow processing + await Task.Delay(TimeSpan.FromSeconds(2)); + return new InventoryResult(true, item); } + + // Not enough items. + return new InventoryResult(false, item); + } -} +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs index c035aadde..8e2780100 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs @@ -2,56 +2,55 @@ using Dapr.Workflow; using Microsoft.Extensions.Logging; -namespace WorkflowConsoleApp.Activities +namespace WorkflowConsoleApp.Activities; + +class UpdateInventoryActivity : WorkflowActivity { - class UpdateInventoryActivity : WorkflowActivity + static readonly string storeName = "statestore"; + readonly ILogger logger; + readonly DaprClient client; + + public UpdateInventoryActivity(ILoggerFactory loggerFactory, DaprClient client) { - static readonly string storeName = "statestore"; - readonly ILogger logger; - readonly DaprClient client; + this.logger = loggerFactory.CreateLogger(); + this.client = client; + } - public UpdateInventoryActivity(ILoggerFactory loggerFactory, DaprClient client) + public override async Task RunAsync(WorkflowActivityContext context, PaymentRequest req) + { + this.logger.LogInformation( + "Checking inventory for order '{requestId}' for {amount} {name}", + req.RequestId, + req.Amount, + req.ItemName); + + // Simulate slow processing + await Task.Delay(TimeSpan.FromSeconds(5)); + + // Determine if there are enough Items for purchase + InventoryItem item = await client.GetStateAsync( + storeName, + req.ItemName.ToLowerInvariant()); + int newQuantity = item.Quantity - req.Amount; + if (newQuantity < 0) { - this.logger = loggerFactory.CreateLogger(); - this.client = client; + this.logger.LogInformation( + "Payment for request ID '{requestId}' could not be processed. Insufficient inventory.", + req.RequestId); + throw new InvalidOperationException($"Not enough '{req.ItemName}' inventory! Requested {req.Amount} but only {item.Quantity} available."); } - public override async Task RunAsync(WorkflowActivityContext context, PaymentRequest req) - { - this.logger.LogInformation( - "Checking inventory for order '{requestId}' for {amount} {name}", - req.RequestId, - req.Amount, - req.ItemName); - - // Simulate slow processing - await Task.Delay(TimeSpan.FromSeconds(5)); - - // Determine if there are enough Items for purchase - InventoryItem item = await client.GetStateAsync( - storeName, - req.ItemName.ToLowerInvariant()); - int newQuantity = item.Quantity - req.Amount; - if (newQuantity < 0) - { - this.logger.LogInformation( - "Payment for request ID '{requestId}' could not be processed. Insufficient inventory.", - req.RequestId); - throw new InvalidOperationException($"Not enough '{req.ItemName}' inventory! Requested {req.Amount} but only {item.Quantity} available."); - } - - // Update the statestore with the new amount of the item - await client.SaveStateAsync( - storeName, - req.ItemName.ToLowerInvariant(), - new InventoryItem(Name: req.ItemName, PerItemCost: item.PerItemCost, Quantity: newQuantity)); + // Update the statestore with the new amount of the item + await client.SaveStateAsync( + storeName, + req.ItemName.ToLowerInvariant(), + new InventoryItem(Name: req.ItemName, PerItemCost: item.PerItemCost, Quantity: newQuantity)); - this.logger.LogInformation( - "There are now {quantity} {name} left in stock", - newQuantity, - item.Name); + this.logger.LogInformation( + "There are now {quantity} {name} left in stock", + newQuantity, + item.Name); - return null; - } + return null; } -} +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Models.cs b/examples/Workflow/WorkflowConsoleApp/Models.cs index 7892c7525..dfa132dda 100644 --- a/examples/Workflow/WorkflowConsoleApp/Models.cs +++ b/examples/Workflow/WorkflowConsoleApp/Models.cs @@ -1,15 +1,14 @@ -namespace WorkflowConsoleApp +namespace WorkflowConsoleApp; + +public record OrderPayload(string Name, double TotalCost, int Quantity = 1); +public record InventoryRequest(string RequestId, string ItemName, int Quantity); +public record InventoryResult(bool Success, InventoryItem orderPayload); +public record PaymentRequest(string RequestId, string ItemName, int Amount, double Currency); +public record OrderResult(bool Processed); +public record InventoryItem(string Name, double PerItemCost, int Quantity); +public enum ApprovalResult { - public record OrderPayload(string Name, double TotalCost, int Quantity = 1); - public record InventoryRequest(string RequestId, string ItemName, int Quantity); - public record InventoryResult(bool Success, InventoryItem orderPayload); - public record PaymentRequest(string RequestId, string ItemName, int Amount, double Currency); - public record OrderResult(bool Processed); - public record InventoryItem(string Name, double PerItemCost, int Quantity); - public enum ApprovalResult - { - Unspecified = 0, - Approved = 1, - Rejected = 2, - } -} + Unspecified = 0, + Approved = 1, + Rejected = 2, +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs b/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs index 8c5e9d133..988c9693a 100644 --- a/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs +++ b/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs @@ -2,122 +2,121 @@ using Microsoft.Extensions.Logging; using WorkflowConsoleApp.Activities; -namespace WorkflowConsoleApp.Workflows +namespace WorkflowConsoleApp.Workflows; + +public class OrderProcessingWorkflow : Workflow { - public class OrderProcessingWorkflow : Workflow + readonly WorkflowTaskOptions defaultActivityRetryOptions = new WorkflowTaskOptions { - readonly WorkflowTaskOptions defaultActivityRetryOptions = new WorkflowTaskOptions - { - // NOTE: Beware that changing the number of retries is a breaking change for existing workflows. - RetryPolicy = new WorkflowRetryPolicy( - maxNumberOfAttempts: 3, - firstRetryInterval: TimeSpan.FromSeconds(5)), - }; + // NOTE: Beware that changing the number of retries is a breaking change for existing workflows. + RetryPolicy = new WorkflowRetryPolicy( + maxNumberOfAttempts: 3, + firstRetryInterval: TimeSpan.FromSeconds(5)), + }; - public override async Task RunAsync(WorkflowContext context, OrderPayload order) - { - string orderId = context.InstanceId; - var logger = context.CreateReplaySafeLogger(); + public override async Task RunAsync(WorkflowContext context, OrderPayload order) + { + string orderId = context.InstanceId; + var logger = context.CreateReplaySafeLogger(); - logger.LogInformation("Received order {orderId} for {quantity} {name} at ${totalCost}", orderId, order.Quantity, order.Name, order.TotalCost); + logger.LogInformation("Received order {orderId} for {quantity} {name} at ${totalCost}", orderId, order.Quantity, order.Name, order.TotalCost); - // Notify the user that an order has come through - await context.CallActivityAsync( - nameof(NotifyActivity), - new Notification($"Received order {orderId} for {order.Quantity} {order.Name} at ${order.TotalCost}")); + // Notify the user that an order has come through + await context.CallActivityAsync( + nameof(NotifyActivity), + new Notification($"Received order {orderId} for {order.Quantity} {order.Name} at ${order.TotalCost}")); - // Determine if there is enough of the item available for purchase by checking the inventory - InventoryResult result = await context.CallActivityAsync( - nameof(ReserveInventoryActivity), - new InventoryRequest(RequestId: orderId, order.Name, order.Quantity), - this.defaultActivityRetryOptions); + // Determine if there is enough of the item available for purchase by checking the inventory + InventoryResult result = await context.CallActivityAsync( + nameof(ReserveInventoryActivity), + new InventoryRequest(RequestId: orderId, order.Name, order.Quantity), + this.defaultActivityRetryOptions); - // If there is insufficient inventory, fail and let the user know - if (!result.Success) - { - logger.LogError("Insufficient inventory for {orderName}", order.Name); + // If there is insufficient inventory, fail and let the user know + if (!result.Success) + { + logger.LogError("Insufficient inventory for {orderName}", order.Name); - // End the workflow here since we don't have sufficient inventory - await context.CallActivityAsync( - nameof(NotifyActivity), - new Notification($"Insufficient inventory for {order.Name}")); - return new OrderResult(Processed: false); - } + // End the workflow here since we don't have sufficient inventory + await context.CallActivityAsync( + nameof(NotifyActivity), + new Notification($"Insufficient inventory for {order.Name}")); + return new OrderResult(Processed: false); + } - // Require orders over a certain threshold to be approved - const int threshold = 50000; - if (order.TotalCost > threshold) - { - logger.LogInformation("Requesting manager approval since total cost {totalCost} exceeds threshold {threshold}", order.TotalCost, threshold); - // Request manager approval for the order - await context.CallActivityAsync(nameof(RequestApprovalActivity), order); + // Require orders over a certain threshold to be approved + const int threshold = 50000; + if (order.TotalCost > threshold) + { + logger.LogInformation("Requesting manager approval since total cost {totalCost} exceeds threshold {threshold}", order.TotalCost, threshold); + // Request manager approval for the order + await context.CallActivityAsync(nameof(RequestApprovalActivity), order); - try - { - // Pause and wait for a manager to approve the order - context.SetCustomStatus("Waiting for approval"); - ApprovalResult approvalResult = await context.WaitForExternalEventAsync( - eventName: "ManagerApproval", - timeout: TimeSpan.FromSeconds(30)); + try + { + // Pause and wait for a manager to approve the order + context.SetCustomStatus("Waiting for approval"); + ApprovalResult approvalResult = await context.WaitForExternalEventAsync( + eventName: "ManagerApproval", + timeout: TimeSpan.FromSeconds(30)); - logger.LogInformation("Approval result: {approvalResult}", approvalResult); - context.SetCustomStatus($"Approval result: {approvalResult}"); - if (approvalResult == ApprovalResult.Rejected) - { - logger.LogWarning("Order was rejected by approver"); - - // The order was rejected, end the workflow here - await context.CallActivityAsync( - nameof(NotifyActivity), - new Notification($"Order was rejected by approver")); - return new OrderResult(Processed: false); - } - } - catch (TaskCanceledException) + logger.LogInformation("Approval result: {approvalResult}", approvalResult); + context.SetCustomStatus($"Approval result: {approvalResult}"); + if (approvalResult == ApprovalResult.Rejected) { - logger.LogError("Cancelling order because it didn't receive an approval"); - - // An approval timeout results in automatic order cancellation + logger.LogWarning("Order was rejected by approver"); + + // The order was rejected, end the workflow here await context.CallActivityAsync( nameof(NotifyActivity), - new Notification($"Cancelling order because it didn't receive an approval")); + new Notification($"Order was rejected by approver")); return new OrderResult(Processed: false); } } - - // There is enough inventory available so the user can purchase the item(s). Process their payment - logger.LogInformation("Processing payment as sufficient inventory is available"); - await context.CallActivityAsync( - nameof(ProcessPaymentActivity), - new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost), - this.defaultActivityRetryOptions); - - try + catch (TaskCanceledException) { - // There is enough inventory available so the user can purchase the item(s). Process their payment - await context.CallActivityAsync( - nameof(UpdateInventoryActivity), - new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost), - this.defaultActivityRetryOptions); - } - catch (WorkflowTaskFailedException e) - { - // Let them know their payment processing failed - logger.LogError("Order {orderId} failed! Details: {errorMessage}", orderId, e.FailureDetails.ErrorMessage); + logger.LogError("Cancelling order because it didn't receive an approval"); + + // An approval timeout results in automatic order cancellation await context.CallActivityAsync( nameof(NotifyActivity), - new Notification($"Order {orderId} Failed! Details: {e.FailureDetails.ErrorMessage}")); + new Notification($"Cancelling order because it didn't receive an approval")); return new OrderResult(Processed: false); } + } + + // There is enough inventory available so the user can purchase the item(s). Process their payment + logger.LogInformation("Processing payment as sufficient inventory is available"); + await context.CallActivityAsync( + nameof(ProcessPaymentActivity), + new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost), + this.defaultActivityRetryOptions); - // Let them know their payment was processed - logger.LogError("Order {orderId} has completed!", orderId); + try + { + // There is enough inventory available so the user can purchase the item(s). Process their payment + await context.CallActivityAsync( + nameof(UpdateInventoryActivity), + new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost), + this.defaultActivityRetryOptions); + } + catch (WorkflowTaskFailedException e) + { + // Let them know their payment processing failed + logger.LogError("Order {orderId} failed! Details: {errorMessage}", orderId, e.FailureDetails.ErrorMessage); await context.CallActivityAsync( nameof(NotifyActivity), - new Notification($"Order {orderId} has completed!")); - - // End the workflow with a success result - return new OrderResult(Processed: true); + new Notification($"Order {orderId} Failed! Details: {e.FailureDetails.ErrorMessage}")); + return new OrderResult(Processed: false); } + + // Let them know their payment was processed + logger.LogError("Order {orderId} has completed!", orderId); + await context.CallActivityAsync( + nameof(NotifyActivity), + new Notification($"Order {orderId} has completed!")); + + // End the workflow with a success result + return new OrderResult(Processed: true); } -} +} \ No newline at end of file diff --git a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs index 3093e5c7a..de511ba7b 100644 --- a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs +++ b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs @@ -6,80 +6,79 @@ using WorkflowConsoleApp.Workflows; using Xunit; -namespace WorkflowUnitTest +namespace WorkflowUnitTest; + +[Trait("Example", "true")] +public class OrderProcessingTests { - [Trait("Example", "true")] - public class OrderProcessingTests + [Fact] + public async Task TestSuccessfulOrder() { - [Fact] - public async Task TestSuccessfulOrder() - { - // Test payloads - OrderPayload order = new(Name: "Paperclips", TotalCost: 99.95, Quantity: 10); - PaymentRequest expectedPaymentRequest = new(It.IsAny(), order.Name, order.Quantity, order.TotalCost); - InventoryRequest expectedInventoryRequest = new(It.IsAny(), order.Name, order.Quantity); - InventoryResult inventoryResult = new(Success: true, new InventoryItem(order.Name, 9.99, order.Quantity)); + // Test payloads + OrderPayload order = new(Name: "Paperclips", TotalCost: 99.95, Quantity: 10); + PaymentRequest expectedPaymentRequest = new(It.IsAny(), order.Name, order.Quantity, order.TotalCost); + InventoryRequest expectedInventoryRequest = new(It.IsAny(), order.Name, order.Quantity); + InventoryResult inventoryResult = new(Success: true, new InventoryItem(order.Name, 9.99, order.Quantity)); - // Mock the call to ReserveInventoryActivity - Mock mockContext = new(); - mockContext - .Setup(ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(inventoryResult)); + // Mock the call to ReserveInventoryActivity + Mock mockContext = new(); + mockContext + .Setup(ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(inventoryResult)); - // Run the workflow directly - OrderResult result = await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); + // Run the workflow directly + OrderResult result = await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); - // Verify that workflow result matches what we expect - Assert.NotNull(result); - Assert.True(result.Processed); + // Verify that workflow result matches what we expect + Assert.NotNull(result); + Assert.True(result.Processed); - // Verify that ReserveInventoryActivity was called with a specific input - mockContext.Verify( - ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), expectedInventoryRequest, It.IsAny()), - Times.Once()); + // Verify that ReserveInventoryActivity was called with a specific input + mockContext.Verify( + ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), expectedInventoryRequest, It.IsAny()), + Times.Once()); - // Verify that ProcessPaymentActivity was called with a specific input - mockContext.Verify( - ctx => ctx.CallActivityAsync(nameof(ProcessPaymentActivity), expectedPaymentRequest, It.IsAny()), - Times.Once()); + // Verify that ProcessPaymentActivity was called with a specific input + mockContext.Verify( + ctx => ctx.CallActivityAsync(nameof(ProcessPaymentActivity), expectedPaymentRequest, It.IsAny()), + Times.Once()); - // Verify that there were two calls to NotifyActivity - mockContext.Verify( - ctx => ctx.CallActivityAsync(nameof(NotifyActivity), It.IsAny(), It.IsAny()), - Times.Exactly(2)); - } + // Verify that there were two calls to NotifyActivity + mockContext.Verify( + ctx => ctx.CallActivityAsync(nameof(NotifyActivity), It.IsAny(), It.IsAny()), + Times.Exactly(2)); + } - [Fact] - public async Task TestInsufficientInventory() - { - // Test payloads - OrderPayload order = new(Name: "Paperclips", TotalCost: 99.95, Quantity: int.MaxValue); - InventoryRequest expectedInventoryRequest = new(It.IsAny(), order.Name, order.Quantity); - InventoryResult inventoryResult = new(Success: false, null); + [Fact] + public async Task TestInsufficientInventory() + { + // Test payloads + OrderPayload order = new(Name: "Paperclips", TotalCost: 99.95, Quantity: int.MaxValue); + InventoryRequest expectedInventoryRequest = new(It.IsAny(), order.Name, order.Quantity); + InventoryResult inventoryResult = new(Success: false, null); - // Mock the call to ReserveInventoryActivity - Mock mockContext = new(); - mockContext - .Setup(ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(inventoryResult)); + // Mock the call to ReserveInventoryActivity + Mock mockContext = new(); + mockContext + .Setup(ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(inventoryResult)); - // Run the workflow directly - await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); + // Run the workflow directly + await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); - // Verify that ReserveInventoryActivity was called with a specific input - mockContext.Verify( - ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), expectedInventoryRequest, It.IsAny()), - Times.Once()); + // Verify that ReserveInventoryActivity was called with a specific input + mockContext.Verify( + ctx => ctx.CallActivityAsync(nameof(ReserveInventoryActivity), expectedInventoryRequest, It.IsAny()), + Times.Once()); - // Verify that ProcessPaymentActivity was never called - mockContext.Verify( - ctx => ctx.CallActivityAsync(nameof(ProcessPaymentActivity), It.IsAny(), It.IsAny()), - Times.Never()); + // Verify that ProcessPaymentActivity was never called + mockContext.Verify( + ctx => ctx.CallActivityAsync(nameof(ProcessPaymentActivity), It.IsAny(), It.IsAny()), + Times.Never()); - // Verify that there were two calls to NotifyActivity - mockContext.Verify( - ctx => ctx.CallActivityAsync(nameof(NotifyActivity), It.IsAny(), It.IsAny()), - Times.Exactly(2)); - } + // Verify that there were two calls to NotifyActivity + mockContext.Verify( + ctx => ctx.CallActivityAsync(nameof(NotifyActivity), It.IsAny(), It.IsAny()), + Times.Exactly(2)); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs b/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs index 574a172a8..132ca5486 100644 --- a/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs +++ b/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs @@ -21,170 +21,170 @@ using Microsoft.Extensions.DependencyInjection; using System.Diagnostics; -namespace Microsoft.AspNetCore.Builder +namespace Microsoft.AspNetCore.Builder; + +/// +/// Contains extension methods for using Dapr Actors with endpoint routing. +/// +public static class ActorsEndpointRouteBuilderExtensions { /// - /// Contains extension methods for using Dapr Actors with endpoint routing. + /// Maps endpoints for Dapr Actors into the . /// - public static class ActorsEndpointRouteBuilderExtensions + /// The . + /// + /// An that can be used to configure the endpoints. + /// + public static IEndpointConventionBuilder MapActorsHandlers(this IEndpointRouteBuilder endpoints) { - /// - /// Maps endpoints for Dapr Actors into the . - /// - /// The . - /// - /// An that can be used to configure the endpoints. - /// - public static IEndpointConventionBuilder MapActorsHandlers(this IEndpointRouteBuilder endpoints) + if (endpoints.ServiceProvider.GetService() is null) { - if (endpoints.ServiceProvider.GetService() is null) - { - throw new InvalidOperationException( - "The ActorRuntime service is not registered with the dependency injection container. " + - "Call AddActors() inside ConfigureServices() to register the actor runtime and actor types."); - } - - var builders = new[] - { - MapDaprConfigEndpoint(endpoints), - MapActorDeactivationEndpoint(endpoints), - MapActorMethodEndpoint(endpoints), - MapReminderEndpoint(endpoints), - MapTimerEndpoint(endpoints), - MapActorHealthChecks(endpoints) - }; - - return new CompositeEndpointConventionBuilder(builders); + throw new InvalidOperationException( + "The ActorRuntime service is not registered with the dependency injection container. " + + "Call AddActors() inside ConfigureServices() to register the actor runtime and actor types."); } - private static IEndpointConventionBuilder MapDaprConfigEndpoint(this IEndpointRouteBuilder endpoints) + var builders = new[] { - var runtime = endpoints.ServiceProvider.GetRequiredService(); - return endpoints.MapGet("dapr/config", async context => - { - context.Response.ContentType = "application/json"; - await runtime.SerializeSettingsAndRegisteredTypes(context.Response.BodyWriter); - await context.Response.BodyWriter.FlushAsync(); - }).WithDisplayName(b => "Dapr Actors Config"); - } + MapDaprConfigEndpoint(endpoints), + MapActorDeactivationEndpoint(endpoints), + MapActorMethodEndpoint(endpoints), + MapReminderEndpoint(endpoints), + MapTimerEndpoint(endpoints), + MapActorHealthChecks(endpoints) + }; + + return new CompositeEndpointConventionBuilder(builders); + } - private static IEndpointConventionBuilder MapActorDeactivationEndpoint(this IEndpointRouteBuilder endpoints) + private static IEndpointConventionBuilder MapDaprConfigEndpoint(this IEndpointRouteBuilder endpoints) + { + var runtime = endpoints.ServiceProvider.GetRequiredService(); + return endpoints.MapGet("dapr/config", async context => { - var runtime = endpoints.ServiceProvider.GetRequiredService(); - return endpoints.MapDelete("actors/{actorTypeName}/{actorId}", async context => - { - var routeValues = context.Request.RouteValues; - var actorTypeName = (string)routeValues["actorTypeName"]; - var actorId = (string)routeValues["actorId"]; - await runtime.DeactivateAsync(actorTypeName, actorId); - }).WithDisplayName(b => "Dapr Actors Deactivation"); - } + context.Response.ContentType = "application/json"; + await runtime.SerializeSettingsAndRegisteredTypes(context.Response.BodyWriter); + await context.Response.BodyWriter.FlushAsync(); + }).WithDisplayName(b => "Dapr Actors Config"); + } + + private static IEndpointConventionBuilder MapActorDeactivationEndpoint(this IEndpointRouteBuilder endpoints) + { + var runtime = endpoints.ServiceProvider.GetRequiredService(); + return endpoints.MapDelete("actors/{actorTypeName}/{actorId}", async context => + { + var routeValues = context.Request.RouteValues; + var actorTypeName = (string)routeValues["actorTypeName"]; + var actorId = (string)routeValues["actorId"]; + await runtime.DeactivateAsync(actorTypeName, actorId); + }).WithDisplayName(b => "Dapr Actors Deactivation"); + } - private static IEndpointConventionBuilder MapActorMethodEndpoint(this IEndpointRouteBuilder endpoints) + private static IEndpointConventionBuilder MapActorMethodEndpoint(this IEndpointRouteBuilder endpoints) + { + var runtime = endpoints.ServiceProvider.GetRequiredService(); + return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/{methodName}", async context => { - var runtime = endpoints.ServiceProvider.GetRequiredService(); - return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/{methodName}", async context => + var routeValues = context.Request.RouteValues; + var actorTypeName = (string)routeValues["actorTypeName"]; + var actorId = (string)routeValues["actorId"]; + var methodName = (string)routeValues["methodName"]; + + if (context.Request.Headers.ContainsKey(Constants.ReentrancyRequestHeaderName)) { - var routeValues = context.Request.RouteValues; - var actorTypeName = (string)routeValues["actorTypeName"]; - var actorId = (string)routeValues["actorId"]; - var methodName = (string)routeValues["methodName"]; + var daprReentrancyHeader = context.Request.Headers[Constants.ReentrancyRequestHeaderName]; + ActorReentrancyContextAccessor.ReentrancyContext = daprReentrancyHeader; + } - if (context.Request.Headers.ContainsKey(Constants.ReentrancyRequestHeaderName)) - { - var daprReentrancyHeader = context.Request.Headers[Constants.ReentrancyRequestHeaderName]; - ActorReentrancyContextAccessor.ReentrancyContext = daprReentrancyHeader; - } + // If Header is present, call is made using Remoting, use Remoting dispatcher. + if (context.Request.Headers.ContainsKey(Constants.RequestHeaderName)) + { + var daprActorheader = context.Request.Headers[Constants.RequestHeaderName]; - // If Header is present, call is made using Remoting, use Remoting dispatcher. - if (context.Request.Headers.ContainsKey(Constants.RequestHeaderName)) + try { - var daprActorheader = context.Request.Headers[Constants.RequestHeaderName]; + var (header, body) = await runtime.DispatchWithRemotingAsync(actorTypeName, actorId, methodName, daprActorheader, context.Request.Body, context.RequestAborted); - try + // Item 1 is header , Item 2 is body + if (header != string.Empty) { - var (header, body) = await runtime.DispatchWithRemotingAsync(actorTypeName, actorId, methodName, daprActorheader, context.Request.Body, context.RequestAborted); - - // Item 1 is header , Item 2 is body - if (header != string.Empty) - { - // exception case - context.Response.Headers[Constants.ErrorResponseHeaderName] = header; // add error header - } - - await context.Response.Body.WriteAsync(body, 0, body.Length, context.RequestAborted); // add response message body + // exception case + context.Response.Headers[Constants.ErrorResponseHeaderName] = header; // add error header } - catch (Exception ex) - { - var (header, body) = CreateExceptionResponseMessage(ex); - context.Response.Headers[Constants.ErrorResponseHeaderName] = header; - await context.Response.Body.WriteAsync(body, 0, body.Length, context.RequestAborted); - } - finally - { - ActorReentrancyContextAccessor.ReentrancyContext = null; - } + await context.Response.Body.WriteAsync(body, 0, body.Length, context.RequestAborted); // add response message body } - else + catch (Exception ex) { - try - { - await runtime.DispatchWithoutRemotingAsync(actorTypeName, actorId, methodName, context.Request.Body, context.Response.Body, context.RequestAborted); - } - finally - { - ActorReentrancyContextAccessor.ReentrancyContext = null; - } + var (header, body) = CreateExceptionResponseMessage(ex); + + context.Response.Headers[Constants.ErrorResponseHeaderName] = header; + await context.Response.Body.WriteAsync(body, 0, body.Length, context.RequestAborted); } - }).WithDisplayName(b => "Dapr Actors Invoke"); - } + finally + { + ActorReentrancyContextAccessor.ReentrancyContext = null; + } + } + else + { + try + { + await runtime.DispatchWithoutRemotingAsync(actorTypeName, actorId, methodName, context.Request.Body, context.Response.Body, context.RequestAborted); + } + finally + { + ActorReentrancyContextAccessor.ReentrancyContext = null; + } + } + }).WithDisplayName(b => "Dapr Actors Invoke"); + } - private static IEndpointConventionBuilder MapReminderEndpoint(this IEndpointRouteBuilder endpoints) + private static IEndpointConventionBuilder MapReminderEndpoint(this IEndpointRouteBuilder endpoints) + { + var runtime = endpoints.ServiceProvider.GetRequiredService(); + return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/remind/{reminderName}", async context => { - var runtime = endpoints.ServiceProvider.GetRequiredService(); - return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/remind/{reminderName}", async context => - { - var routeValues = context.Request.RouteValues; - var actorTypeName = (string)routeValues["actorTypeName"]; - var actorId = (string)routeValues["actorId"]; - var reminderName = (string)routeValues["reminderName"]; - - // read dueTime, period and data from Request Body. - await runtime.FireReminderAsync(actorTypeName, actorId, reminderName, context.Request.Body); - }).WithDisplayName(b => "Dapr Actors Reminder"); - } + var routeValues = context.Request.RouteValues; + var actorTypeName = (string)routeValues["actorTypeName"]; + var actorId = (string)routeValues["actorId"]; + var reminderName = (string)routeValues["reminderName"]; + + // read dueTime, period and data from Request Body. + await runtime.FireReminderAsync(actorTypeName, actorId, reminderName, context.Request.Body); + }).WithDisplayName(b => "Dapr Actors Reminder"); + } - private static IEndpointConventionBuilder MapTimerEndpoint(this IEndpointRouteBuilder endpoints) + private static IEndpointConventionBuilder MapTimerEndpoint(this IEndpointRouteBuilder endpoints) + { + var runtime = endpoints.ServiceProvider.GetRequiredService(); + return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/timer/{timerName}", async context => { - var runtime = endpoints.ServiceProvider.GetRequiredService(); - return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/timer/{timerName}", async context => - { - var routeValues = context.Request.RouteValues; - var actorTypeName = (string)routeValues["actorTypeName"]; - var actorId = (string)routeValues["actorId"]; - var timerName = (string)routeValues["timerName"]; - - // read dueTime, period and data from Request Body. - await runtime.FireTimerAsync(actorTypeName, actorId, timerName, context.Request.Body); - }).WithDisplayName(b => "Dapr Actors Timer"); - } + var routeValues = context.Request.RouteValues; + var actorTypeName = (string)routeValues["actorTypeName"]; + var actorId = (string)routeValues["actorId"]; + var timerName = (string)routeValues["timerName"]; + + // read dueTime, period and data from Request Body. + await runtime.FireTimerAsync(actorTypeName, actorId, timerName, context.Request.Body); + }).WithDisplayName(b => "Dapr Actors Timer"); + } - private static IEndpointConventionBuilder MapActorHealthChecks(this IEndpointRouteBuilder endpoints) + private static IEndpointConventionBuilder MapActorHealthChecks(this IEndpointRouteBuilder endpoints) + { + var builder = endpoints.MapHealthChecks("/healthz").WithMetadata(new AllowAnonymousAttribute()); + builder.Add(b => { - var builder = endpoints.MapHealthChecks("/healthz").WithMetadata(new AllowAnonymousAttribute()); - builder.Add(b => - { - // Sets the route order so that this is matched with lower priority than an endpoint - // configured by default. - // - // This is necessary because it allows a user defined `/healthz` endpoint to win in the - // most common cases, but still fulfills Dapr's contract when the user doesn't have - // a health check at `/healthz`. - ((RouteEndpointBuilder)b).Order = 100; - }); - return builder.WithDisplayName(b => "Dapr Actors Health Check"); - } + // Sets the route order so that this is matched with lower priority than an endpoint + // configured by default. + // + // This is necessary because it allows a user defined `/healthz` endpoint to win in the + // most common cases, but still fulfills Dapr's contract when the user doesn't have + // a health check at `/healthz`. + ((RouteEndpointBuilder)b).Order = 100; + }); + return builder.WithDisplayName(b => "Dapr Actors Health Check"); + } private static Tuple CreateExceptionResponseMessage(Exception ex) { @@ -210,20 +210,19 @@ private static string GetExceptionInfo(Exception ex) { return $"Exception: {ex.GetType().Name}, Method Name: {frame.GetMethod().Name}, Line Number: {frame.GetFileLineNumber()}, Exception uuid: {Guid.NewGuid().ToString()}"; } private class CompositeEndpointConventionBuilder : IEndpointConventionBuilder - { - private readonly IEndpointConventionBuilder[] inner; + { + private readonly IEndpointConventionBuilder[] inner; - public CompositeEndpointConventionBuilder(IEndpointConventionBuilder[] inner) - { - this.inner = inner; - } + public CompositeEndpointConventionBuilder(IEndpointConventionBuilder[] inner) + { + this.inner = inner; + } - public void Add(Action convention) + public void Add(Action convention) + { + for (var i = 0; i < inner.Length; i++) { - for (var i = 0; i < inner.Length; i++) - { - inner[i].Add(convention); - } + inner[i].Add(convention); } } } diff --git a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs index 9b80975db..278df4bb6 100644 --- a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs +++ b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs @@ -22,91 +22,90 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +/// +/// Contains extension methods for using Dapr Actors with dependency injection. +/// +public static class ActorsServiceCollectionExtensions { /// - /// Contains extension methods for using Dapr Actors with dependency injection. + /// Adds Dapr Actors support to the service collection. /// - public static class ActorsServiceCollectionExtensions + /// The . + /// A delegate used to configure actor options and register actor types. + /// The lifetime of the registered services. + public static void AddActors(this IServiceCollection? services, Action? configure, ServiceLifetime lifetime = ServiceLifetime.Singleton) { - /// - /// Adds Dapr Actors support to the service collection. - /// - /// The . - /// A delegate used to configure actor options and register actor types. - /// The lifetime of the registered services. - public static void AddActors(this IServiceCollection? services, Action? configure, ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - ArgumentNullException.ThrowIfNull(services, nameof(services)); + ArgumentNullException.ThrowIfNull(services, nameof(services)); - // Routing, health checks and logging are required dependencies. - services.AddRouting(); - services.AddHealthChecks(); - services.AddLogging(); + // Routing, health checks and logging are required dependencies. + services.AddRouting(); + services.AddHealthChecks(); + services.AddLogging(); - var actorRuntimeRegistration = new Func(s => - { - var options = s.GetRequiredService>().Value; - ConfigureActorOptions(s, options); + var actorRuntimeRegistration = new Func(s => + { + var options = s.GetRequiredService>().Value; + ConfigureActorOptions(s, options); - var loggerFactory = s.GetRequiredService(); - var activatorFactory = s.GetRequiredService(); - var proxyFactory = s.GetRequiredService(); - return new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - }); - var proxyFactoryRegistration = new Func(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - ConfigureActorOptions(serviceProvider, options); + var loggerFactory = s.GetRequiredService(); + var activatorFactory = s.GetRequiredService(); + var proxyFactory = s.GetRequiredService(); + return new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + }); + var proxyFactoryRegistration = new Func(serviceProvider => + { + var options = serviceProvider.GetRequiredService>().Value; + ConfigureActorOptions(serviceProvider, options); - var factory = new ActorProxyFactory() - { - DefaultOptions = - { - JsonSerializerOptions = options.JsonSerializerOptions, - DaprApiToken = options.DaprApiToken, - HttpEndpoint = options.HttpEndpoint, - } - }; + var factory = new ActorProxyFactory() + { + DefaultOptions = + { + JsonSerializerOptions = options.JsonSerializerOptions, + DaprApiToken = options.DaprApiToken, + HttpEndpoint = options.HttpEndpoint, + } + }; - return factory; - }); + return factory; + }); - switch (lifetime) - { - case ServiceLifetime.Scoped: - services.TryAddScoped(); - services.TryAddScoped(actorRuntimeRegistration); - services.TryAddScoped(proxyFactoryRegistration); - break; - case ServiceLifetime.Transient: - services.TryAddTransient(); - services.TryAddTransient(actorRuntimeRegistration); - services.TryAddTransient(proxyFactoryRegistration); - break; - default: - case ServiceLifetime.Singleton: - services.TryAddSingleton(); - services.TryAddSingleton(actorRuntimeRegistration); - services.TryAddSingleton(proxyFactoryRegistration); - break; - } - - if (configure != null) - { - services.Configure(configure); - } + switch (lifetime) + { + case ServiceLifetime.Scoped: + services.TryAddScoped(); + services.TryAddScoped(actorRuntimeRegistration); + services.TryAddScoped(proxyFactoryRegistration); + break; + case ServiceLifetime.Transient: + services.TryAddTransient(); + services.TryAddTransient(actorRuntimeRegistration); + services.TryAddTransient(proxyFactoryRegistration); + break; + default: + case ServiceLifetime.Singleton: + services.TryAddSingleton(); + services.TryAddSingleton(actorRuntimeRegistration); + services.TryAddSingleton(proxyFactoryRegistration); + break; } - - private static void ConfigureActorOptions(IServiceProvider serviceProvider, ActorRuntimeOptions options) + + if (configure != null) { - var configuration = serviceProvider.GetService(); - options.DaprApiToken = !string.IsNullOrWhiteSpace(options.DaprApiToken) - ? options.DaprApiToken - : DaprDefaults.GetDefaultDaprApiToken(configuration); - options.HttpEndpoint = !string.IsNullOrWhiteSpace(options.HttpEndpoint) - ? options.HttpEndpoint - : DaprDefaults.GetDefaultHttpEndpoint(); + services.Configure(configure); } } -} + + private static void ConfigureActorOptions(IServiceProvider serviceProvider, ActorRuntimeOptions options) + { + var configuration = serviceProvider.GetService(); + options.DaprApiToken = !string.IsNullOrWhiteSpace(options.DaprApiToken) + ? options.DaprApiToken + : DaprDefaults.GetDefaultDaprApiToken(configuration); + options.HttpEndpoint = !string.IsNullOrWhiteSpace(options.HttpEndpoint) + ? options.HttpEndpoint + : DaprDefaults.GetDefaultHttpEndpoint(); + } +} \ No newline at end of file diff --git a/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivator.cs b/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivator.cs index 46a458f6e..497a279cd 100644 --- a/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivator.cs +++ b/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivator.cs @@ -16,85 +16,84 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +// Implementation of ActorActivator that uses Microsoft.Extensions.DependencyInjection. +internal class DependencyInjectionActorActivator : ActorActivator { - // Implementation of ActorActivator that uses Microsoft.Extensions.DependencyInjection. - internal class DependencyInjectionActorActivator : ActorActivator + private readonly IServiceProvider services; + private readonly ActorTypeInformation type; + private readonly Func initializer; + + // factory is used to create the actor instance - initialization of the factory is protected + // by the initialized and @lock fields. + // + // This serves as a cache for the generated code that constructs the object from DI. + private ObjectFactory factory; + private bool initialized; + private object @lock; + + public DependencyInjectionActorActivator(IServiceProvider services, ActorTypeInformation type) { - private readonly IServiceProvider services; - private readonly ActorTypeInformation type; - private readonly Func initializer; + this.services = services; + this.type = type; - // factory is used to create the actor instance - initialization of the factory is protected - // by the initialized and @lock fields. - // - // This serves as a cache for the generated code that constructs the object from DI. - private ObjectFactory factory; - private bool initialized; - private object @lock; + // Will be invoked to initialize the factory. + initializer = () => + { + return ActivatorUtilities.CreateFactory(this.type.ImplementationType, new Type[]{ typeof(ActorHost), }); + }; + } - public DependencyInjectionActorActivator(IServiceProvider services, ActorTypeInformation type) + public override async Task CreateAsync(ActorHost host) + { + var scope = services.CreateScope(); + try { - this.services = services; - this.type = type; + var factory = LazyInitializer.EnsureInitialized( + ref this.factory, + ref this.initialized, + ref this.@lock, + this.initializer); - // Will be invoked to initialize the factory. - initializer = () => - { - return ActivatorUtilities.CreateFactory(this.type.ImplementationType, new Type[]{ typeof(ActorHost), }); - }; + var actor = (Actor)factory(scope.ServiceProvider, new object[] { host }); + return new State(actor, scope); } - - public override async Task CreateAsync(ActorHost host) + catch { - var scope = services.CreateScope(); - try - { - var factory = LazyInitializer.EnsureInitialized( - ref this.factory, - ref this.initialized, - ref this.@lock, - this.initializer); - - var actor = (Actor)factory(scope.ServiceProvider, new object[] { host }); - return new State(actor, scope); - } - catch - { - // Make sure to clean up the scope if we fail to create the actor; - await DisposeCore(scope); - throw; - } + // Make sure to clean up the scope if we fail to create the actor; + await DisposeCore(scope); + throw; } + } - public override async Task DeleteAsync(ActorActivatorState obj) + public override async Task DeleteAsync(ActorActivatorState obj) + { + var state = (State)obj; + await DisposeCore(state.Actor); + await DisposeCore(state.Scope); + } + + private async ValueTask DisposeCore(object obj) + { + if (obj is IAsyncDisposable asyncDisposable) { - var state = (State)obj; - await DisposeCore(state.Actor); - await DisposeCore(state.Scope); + await asyncDisposable.DisposeAsync(); } - - private async ValueTask DisposeCore(object obj) + else if (obj is IDisposable disposable) { - if (obj is IAsyncDisposable asyncDisposable) - { - await asyncDisposable.DisposeAsync(); - } - else if (obj is IDisposable disposable) - { - disposable.Dispose(); - } + disposable.Dispose(); } + } - private class State : ActorActivatorState + private class State : ActorActivatorState + { + public State(Actor actor, IServiceScope scope) + : base(actor) { - public State(Actor actor, IServiceScope scope) - : base(actor) - { - Scope = scope; - } - - public IServiceScope Scope { get; } + Scope = scope; } + + public IServiceScope Scope { get; } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivatorFactory.cs b/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivatorFactory.cs index f14d54a47..9f4242062 100644 --- a/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivatorFactory.cs +++ b/src/Dapr.Actors.AspNetCore/Runtime/DependencyInjectionActorActivatorFactory.cs @@ -13,21 +13,20 @@ using System; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +// Implementation of ActorActivatorFactory that uses Microsoft.Extensions.DependencyInjection. +internal class DependencyInjectionActorActivatorFactory : ActorActivatorFactory { - // Implementation of ActorActivatorFactory that uses Microsoft.Extensions.DependencyInjection. - internal class DependencyInjectionActorActivatorFactory : ActorActivatorFactory - { - private readonly IServiceProvider services; + private readonly IServiceProvider services; - public DependencyInjectionActorActivatorFactory(IServiceProvider services) - { - this.services = services; - } + public DependencyInjectionActorActivatorFactory(IServiceProvider services) + { + this.services = services; + } - public override ActorActivator CreateActivator(ActorTypeInformation type) - { - return new DependencyInjectionActorActivator(services, type); - } + public override ActorActivator CreateActivator(ActorTypeInformation type) + { + return new DependencyInjectionActorActivator(services, type); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Constants.cs b/src/Dapr.Actors.Generators/Constants.cs index 392def4ef..70e6c8ba0 100644 --- a/src/Dapr.Actors.Generators/Constants.cs +++ b/src/Dapr.Actors.Generators/Constants.cs @@ -1,38 +1,37 @@ -namespace Dapr.Actors.Generators +namespace Dapr.Actors.Generators; + +/// +/// Constants used by the code generator. +/// +internal static class Constants { /// - /// Constants used by the code generator. + /// The namespace used by the generated code. /// - internal static class Constants - { - /// - /// The namespace used by the generated code. - /// - public const string GeneratorsNamespace = "Dapr.Actors.Generators"; + public const string GeneratorsNamespace = "Dapr.Actors.Generators"; - /// - /// The name of the attribute used to mark actor interfaces. - /// - public const string ActorMethodAttributeTypeName = "ActorMethodAttribute"; + /// + /// The name of the attribute used to mark actor interfaces. + /// + public const string ActorMethodAttributeTypeName = "ActorMethodAttribute"; - /// - /// The full type name of the attribute used to mark actor interfaces. - /// - public const string ActorMethodAttributeFullTypeName = GeneratorsNamespace + "." + ActorMethodAttributeTypeName; + /// + /// The full type name of the attribute used to mark actor interfaces. + /// + public const string ActorMethodAttributeFullTypeName = GeneratorsNamespace + "." + ActorMethodAttributeTypeName; - /// - /// The name of the attribute used to mark actor interfaces. - /// - public const string GenerateActorClientAttributeTypeName = "GenerateActorClientAttribute"; + /// + /// The name of the attribute used to mark actor interfaces. + /// + public const string GenerateActorClientAttributeTypeName = "GenerateActorClientAttribute"; - /// - /// The full type name of the attribute used to mark actor interfaces. - /// - public const string GenerateActorClientAttributeFullTypeName = GeneratorsNamespace + "." + GenerateActorClientAttributeTypeName; + /// + /// The full type name of the attribute used to mark actor interfaces. + /// + public const string GenerateActorClientAttributeFullTypeName = GeneratorsNamespace + "." + GenerateActorClientAttributeTypeName; - /// - /// Actor proxy type name. - /// - public const string ActorProxyTypeName = "Dapr.Actors.Client.ActorProxy"; - } -} + /// + /// Actor proxy type name. + /// + public const string ActorProxyTypeName = "Dapr.Actors.Client.ActorProxy"; +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs index 376bb360f..fc21dfa57 100644 --- a/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs +++ b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs @@ -1,25 +1,24 @@ using Microsoft.CodeAnalysis; -namespace Dapr.Actors.Generators.Diagnostics +namespace Dapr.Actors.Generators.Diagnostics; + +internal static class CancellationTokensMustBeTheLastArgument { - internal static class CancellationTokensMustBeTheLastArgument - { - public const string DiagnosticId = "DAPR0001"; - public const string Title = "Invalid method signature"; - public const string MessageFormat = "Cancellation tokens must be the last argument"; - public const string Category = "Usage"; + public const string DiagnosticId = "DAPR0001"; + public const string Title = "Invalid method signature"; + public const string MessageFormat = "Cancellation tokens must be the last argument"; + public const string Category = "Usage"; - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - DiagnosticId, - Title, - MessageFormat, - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true); + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + DiagnosticId, + Title, + MessageFormat, + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true); - internal static Diagnostic CreateDiagnostic(ISymbol symbol) => Diagnostic.Create( - Rule, - symbol.Locations.First(), - symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); - } -} + internal static Diagnostic CreateDiagnostic(ISymbol symbol) => Diagnostic.Create( + Rule, + symbol.Locations.First(), + symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs b/src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs index c82b20630..76c8f7abe 100644 --- a/src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs +++ b/src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs @@ -1,25 +1,24 @@ using Microsoft.CodeAnalysis; -namespace Dapr.Actors.Generators.Diagnostics +namespace Dapr.Actors.Generators.Diagnostics; + +internal static class MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken { - internal static class MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken - { - public const string DiagnosticId = "DAPR0002"; - public const string Title = "Invalid method signature"; - public const string MessageFormat = "Only methods with a single argument or a single argument followed by a cancellation token are supported"; - public const string Category = "Usage"; + public const string DiagnosticId = "DAPR0002"; + public const string Title = "Invalid method signature"; + public const string MessageFormat = "Only methods with a single argument or a single argument followed by a cancellation token are supported"; + public const string Category = "Usage"; - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - DiagnosticId, - Title, - MessageFormat, - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true); + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + DiagnosticId, + Title, + MessageFormat, + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true); - internal static Diagnostic CreateDiagnostic(ISymbol symbol) => Diagnostic.Create( - Rule, - symbol.Locations.First(), - symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); - } -} + internal static Diagnostic CreateDiagnostic(ISymbol symbol) => Diagnostic.Create( + Rule, + symbol.Locations.First(), + symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/DiagnosticsException.cs b/src/Dapr.Actors.Generators/DiagnosticsException.cs index d196f8484..ddbb3b45b 100644 --- a/src/Dapr.Actors.Generators/DiagnosticsException.cs +++ b/src/Dapr.Actors.Generators/DiagnosticsException.cs @@ -1,25 +1,24 @@ using Microsoft.CodeAnalysis; -namespace Dapr.Actors.Generators +namespace Dapr.Actors.Generators; + +/// +/// Exception thrown when diagnostics are encountered during code generation. +/// +internal sealed class DiagnosticsException : Exception { /// - /// Exception thrown when diagnostics are encountered during code generation. + /// Initializes a new instance of the class. /// - internal sealed class DiagnosticsException : Exception + /// List of diagnostics generated. + public DiagnosticsException(IEnumerable diagnostics) + : base(string.Join("\n", diagnostics.Select(d => d.ToString()))) { - /// - /// Initializes a new instance of the class. - /// - /// List of diagnostics generated. - public DiagnosticsException(IEnumerable diagnostics) - : base(string.Join("\n", diagnostics.Select(d => d.ToString()))) - { - this.Diagnostics = diagnostics.ToArray(); - } - - /// - /// Diagnostics encountered during code generation. - /// - public ICollection Diagnostics { get; } + this.Diagnostics = diagnostics.ToArray(); } -} + + /// + /// Diagnostics encountered during code generation. + /// + public ICollection Diagnostics { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index 6b45e86f3..77b0bfd7f 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -1,34 +1,33 @@ -namespace Dapr.Actors.Generators.Extensions +namespace Dapr.Actors.Generators.Extensions; + +internal static class IEnumerableExtensions { - internal static class IEnumerableExtensions + /// + /// Returns the index of the first item in the sequence that satisfies the predicate. If no item satisfies the predicate, -1 is returned. + /// + /// The type of objects in the . + /// in which to search. + /// Function performed to check whether an item satisfies the condition. + /// Return the zero-based index of the first occurrence of an element that satisfies the condition, if found; otherwise, -1. + internal static int IndexOf(this IEnumerable source, Func predicate) { - /// - /// Returns the index of the first item in the sequence that satisfies the predicate. If no item satisfies the predicate, -1 is returned. - /// - /// The type of objects in the . - /// in which to search. - /// Function performed to check whether an item satisfies the condition. - /// Return the zero-based index of the first occurrence of an element that satisfies the condition, if found; otherwise, -1. - internal static int IndexOf(this IEnumerable source, Func predicate) + if (predicate is null) { - if (predicate is null) - { - throw new ArgumentNullException(nameof(predicate)); - } + throw new ArgumentNullException(nameof(predicate)); + } - int index = 0; + int index = 0; - foreach (var item in source) + foreach (var item in source) + { + if (predicate(item)) { - if (predicate(item)) - { - return index; - } - - index++; + return index; } - return -1; + index++; } + + return -1; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 36df7b280..cf4b6ea3a 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -2,158 +2,157 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Dapr.Actors.Generators.Helpers +namespace Dapr.Actors.Generators.Helpers; + +/// +/// Syntax factory helpers for generating syntax. +/// +internal static partial class SyntaxFactoryHelpers { /// - /// Syntax factory helpers for generating syntax. + /// Generates a syntax for an based on the given argument name. /// - internal static partial class SyntaxFactoryHelpers + /// Name of the argument that generated the exception. + /// Returns used to throw an . + public static ThrowExpressionSyntax ThrowArgumentNullException(string argumentName) { - /// - /// Generates a syntax for an based on the given argument name. - /// - /// Name of the argument that generated the exception. - /// Returns used to throw an . - public static ThrowExpressionSyntax ThrowArgumentNullException(string argumentName) - { - return SyntaxFactory.ThrowExpression( - SyntaxFactory.Token(SyntaxKind.ThrowKeyword), - SyntaxFactory.ObjectCreationExpression( - SyntaxFactory.Token(SyntaxKind.NewKeyword), - SyntaxFactory.ParseTypeName("System.ArgumentNullException"), - SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] - { - SyntaxFactory.Argument(NameOfExpression(argumentName)) - })), - default - ) - ); - } - - /// - /// Generates a syntax for null check for the given argument name. - /// - /// Name of the argument whose null check is to be generated. - /// Returns representing an argument null check. - public static IfStatementSyntax ThrowIfArgumentNull(string argumentName) - { - return SyntaxFactory.IfStatement( - SyntaxFactory.BinaryExpression( - SyntaxKind.IsExpression, - SyntaxFactory.IdentifierName(argumentName), - SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression) - ), - SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] + return SyntaxFactory.ThrowExpression( + SyntaxFactory.Token(SyntaxKind.ThrowKeyword), + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.Token(SyntaxKind.NewKeyword), + SyntaxFactory.ParseTypeName("System.ArgumentNullException"), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { - SyntaxFactory.ExpressionStatement(ThrowArgumentNullException(argumentName)) - })) - ); - } + SyntaxFactory.Argument(NameOfExpression(argumentName)) + })), + default + ) + ); + } - /// - /// Generates a syntax for nameof expression for the given argument name. - /// - /// Name of the argument from which the syntax is to be generated. - /// Return a representing a NameOf expression. - public static ExpressionSyntax NameOfExpression(string argumentName) - { - var nameofIdentifier = SyntaxFactory.Identifier( - SyntaxFactory.TriviaList(), - SyntaxKind.NameOfKeyword, - "nameof", - "nameof", - SyntaxFactory.TriviaList()); + /// + /// Generates a syntax for null check for the given argument name. + /// + /// Name of the argument whose null check is to be generated. + /// Returns representing an argument null check. + public static IfStatementSyntax ThrowIfArgumentNull(string argumentName) + { + return SyntaxFactory.IfStatement( + SyntaxFactory.BinaryExpression( + SyntaxKind.IsExpression, + SyntaxFactory.IdentifierName(argumentName), + SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression) + ), + SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] + { + SyntaxFactory.ExpressionStatement(ThrowArgumentNullException(argumentName)) + })) + ); + } - return SyntaxFactory.InvocationExpression( - SyntaxFactory.IdentifierName(nameofIdentifier), - SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] - { - SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName)) - })) - ); - } + /// + /// Generates a syntax for nameof expression for the given argument name. + /// + /// Name of the argument from which the syntax is to be generated. + /// Return a representing a NameOf expression. + public static ExpressionSyntax NameOfExpression(string argumentName) + { + var nameofIdentifier = SyntaxFactory.Identifier( + SyntaxFactory.TriviaList(), + SyntaxKind.NameOfKeyword, + "nameof", + "nameof", + SyntaxFactory.TriviaList()); - /// - /// Generates the invocation syntax to call a remote method with the actor proxy. - /// - /// Member syntax to access actorProxy member. - /// Name of remote method to invoke. - /// Remote method parameters. - /// Return types of remote method invocation. - /// Returns the representing a call to the actor proxy. - public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync( - MemberAccessExpressionSyntax actorProxyMemberSyntax, - string remoteMethodName, - IEnumerable remoteMethodParameters, - IEnumerable remoteMethodReturnTypes) - { - // Define the type arguments to pass to the actor proxy method invocation. - var proxyInvocationTypeArguments = new List() - .Concat(remoteMethodParameters - .Where(p => p.Type is not { Name: "CancellationToken" }) - .Select(p => SyntaxFactory.ParseTypeName(p.Type.ToString()))) - .Concat(remoteMethodReturnTypes - .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString()))); + return SyntaxFactory.InvocationExpression( + SyntaxFactory.IdentifierName(nameofIdentifier), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] + { + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName)) + })) + ); + } - // Define the arguments to pass to the actor proxy method invocation. - var proxyInvocationArguments = new List() - // Name of remote method to invoke. - .Append(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(remoteMethodName)))) - // Actor method arguments, including the CancellationToken if it exists. - .Concat(remoteMethodParameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name)))); + /// + /// Generates the invocation syntax to call a remote method with the actor proxy. + /// + /// Member syntax to access actorProxy member. + /// Name of remote method to invoke. + /// Remote method parameters. + /// Return types of remote method invocation. + /// Returns the representing a call to the actor proxy. + public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync( + MemberAccessExpressionSyntax actorProxyMemberSyntax, + string remoteMethodName, + IEnumerable remoteMethodParameters, + IEnumerable remoteMethodReturnTypes) + { + // Define the type arguments to pass to the actor proxy method invocation. + var proxyInvocationTypeArguments = new List() + .Concat(remoteMethodParameters + .Where(p => p.Type is not { Name: "CancellationToken" }) + .Select(p => SyntaxFactory.ParseTypeName(p.Type.ToString()))) + .Concat(remoteMethodReturnTypes + .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString()))); - // If the invocation has return types or input parameters, we need to use the generic version of the method. - SimpleNameSyntax invokeAsyncSyntax = proxyInvocationTypeArguments.Any() - ? SyntaxFactory.GenericName( - SyntaxFactory.Identifier("InvokeMethodAsync"), - SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(proxyInvocationTypeArguments))) - : SyntaxFactory.IdentifierName("InvokeMethodAsync"); + // Define the arguments to pass to the actor proxy method invocation. + var proxyInvocationArguments = new List() + // Name of remote method to invoke. + .Append(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(remoteMethodName)))) + // Actor method arguments, including the CancellationToken if it exists. + .Concat(remoteMethodParameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name)))); - // Generate the invocation syntax. - var generatedInvocationSyntax = SyntaxFactory.InvocationExpression( + // If the invocation has return types or input parameters, we need to use the generic version of the method. + SimpleNameSyntax invokeAsyncSyntax = proxyInvocationTypeArguments.Any() + ? SyntaxFactory.GenericName( + SyntaxFactory.Identifier("InvokeMethodAsync"), + SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(proxyInvocationTypeArguments))) + : SyntaxFactory.IdentifierName("InvokeMethodAsync"); + + // Generate the invocation syntax. + var generatedInvocationSyntax = SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, actorProxyMemberSyntax, invokeAsyncSyntax )) - .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))); - - return generatedInvocationSyntax; - } + .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))); - /// - /// Returns the for the specified accessibility. - /// - /// Accessibility to convert into a . - /// Returns the collection of representing the given accessibility. - /// Throws when un unexpected accessibility is passed. - public static ICollection GetSyntaxKinds(Accessibility accessibility) - { - var syntaxKinds = new List(); + return generatedInvocationSyntax; + } - switch (accessibility) - { - case Accessibility.Public: - syntaxKinds.Add(SyntaxKind.PublicKeyword); - break; - case Accessibility.Internal: - syntaxKinds.Add(SyntaxKind.InternalKeyword); - break; - case Accessibility.Private: - syntaxKinds.Add(SyntaxKind.PrivateKeyword); - break; - case Accessibility.Protected: - syntaxKinds.Add(SyntaxKind.ProtectedKeyword); - break; - case Accessibility.ProtectedAndInternal: - syntaxKinds.Add(SyntaxKind.ProtectedKeyword); - syntaxKinds.Add(SyntaxKind.InternalKeyword); - break; - default: - throw new InvalidOperationException("Unexpected accessibility"); - } + /// + /// Returns the for the specified accessibility. + /// + /// Accessibility to convert into a . + /// Returns the collection of representing the given accessibility. + /// Throws when un unexpected accessibility is passed. + public static ICollection GetSyntaxKinds(Accessibility accessibility) + { + var syntaxKinds = new List(); - return syntaxKinds; + switch (accessibility) + { + case Accessibility.Public: + syntaxKinds.Add(SyntaxKind.PublicKeyword); + break; + case Accessibility.Internal: + syntaxKinds.Add(SyntaxKind.InternalKeyword); + break; + case Accessibility.Private: + syntaxKinds.Add(SyntaxKind.PrivateKeyword); + break; + case Accessibility.Protected: + syntaxKinds.Add(SyntaxKind.ProtectedKeyword); + break; + case Accessibility.ProtectedAndInternal: + syntaxKinds.Add(SyntaxKind.ProtectedKeyword); + syntaxKinds.Add(SyntaxKind.InternalKeyword); + break; + default: + throw new InvalidOperationException("Unexpected accessibility"); } + + return syntaxKinds; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs index e1f54fac4..c939cce17 100644 --- a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs +++ b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs @@ -1,46 +1,45 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; -namespace Dapr.Actors.Generators.Models +namespace Dapr.Actors.Generators.Models; + +/// +/// Describes an actor client to generate. +/// +internal record class ActorClientDescriptor : IEquatable { /// - /// Describes an actor client to generate. + /// Gets or sets the symbol representing the actor interface. + /// + public INamedTypeSymbol InterfaceType { get; set; } = null!; + + /// + /// Accessibility of the generated client. + /// + public Accessibility Accessibility { get; set; } + + /// + /// Namespace of the generated client. + /// + public string NamespaceName { get; set; } = string.Empty; + + /// + /// Name of the generated client. + /// + public string ClientTypeName { get; set; } = string.Empty; + + /// + /// Fully qualified type name of the generated client. + /// + public string FullyQualifiedTypeName => $"{NamespaceName}.{ClientTypeName}"; + + /// + /// Methods to generate in the client. + /// + public ImmutableArray Methods { get; set; } = Array.Empty().ToImmutableArray(); + + /// + /// Compilation to use for generating the client. /// - internal record class ActorClientDescriptor : IEquatable - { - /// - /// Gets or sets the symbol representing the actor interface. - /// - public INamedTypeSymbol InterfaceType { get; set; } = null!; - - /// - /// Accessibility of the generated client. - /// - public Accessibility Accessibility { get; set; } - - /// - /// Namespace of the generated client. - /// - public string NamespaceName { get; set; } = string.Empty; - - /// - /// Name of the generated client. - /// - public string ClientTypeName { get; set; } = string.Empty; - - /// - /// Fully qualified type name of the generated client. - /// - public string FullyQualifiedTypeName => $"{NamespaceName}.{ClientTypeName}"; - - /// - /// Methods to generate in the client. - /// - public ImmutableArray Methods { get; set; } = Array.Empty().ToImmutableArray(); - - /// - /// Compilation to use for generating the client. - /// - public Compilation Compilation { get; set; } = null!; - } -} + public Compilation Compilation { get; set; } = null!; +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Templates.cs b/src/Dapr.Actors.Generators/Templates.cs index 6cc4c9f87..a9dd68364 100644 --- a/src/Dapr.Actors.Generators/Templates.cs +++ b/src/Dapr.Actors.Generators/Templates.cs @@ -3,27 +3,27 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; -namespace Dapr.Actors.Generators +namespace Dapr.Actors.Generators; + +/// +/// Templates for generating source code. +/// +internal static partial class Templates { /// - /// Templates for generating source code. + /// Returns the for the ActorMethodAttribute. /// - internal static partial class Templates + /// Namespace where to generate attribute. + /// The representing the ActorMethodAttribute. + /// Throws when destinationNamespace is null. + public static SourceText ActorMethodAttributeSourceText(string destinationNamespace) { - /// - /// Returns the for the ActorMethodAttribute. - /// - /// Namespace where to generate attribute. - /// The representing the ActorMethodAttribute. - /// Throws when destinationNamespace is null. - public static SourceText ActorMethodAttributeSourceText(string destinationNamespace) + if (destinationNamespace is null) { - if (destinationNamespace is null) - { - throw new ArgumentNullException(nameof(destinationNamespace)); - } + throw new ArgumentNullException(nameof(destinationNamespace)); + } - var source = $@" + var source = $@" // #nullable enable @@ -39,27 +39,27 @@ internal sealed class {Constants.ActorMethodAttributeTypeName} : Attribute }} }}"; - return SourceText.From( - SyntaxFactory.ParseCompilationUnit(source) - .NormalizeWhitespace() - .ToFullString(), - Encoding.UTF8); - } + return SourceText.From( + SyntaxFactory.ParseCompilationUnit(source) + .NormalizeWhitespace() + .ToFullString(), + Encoding.UTF8); + } - /// - /// Returns the for the GenerateActorClientAttribute. - /// - /// Namespace where to generate attribute. - /// The representing the ActorMethodAttribute. - /// Throws when destinationNamespace is null. - public static SourceText GenerateActorClientAttributeSourceText(string destinationNamespace) + /// + /// Returns the for the GenerateActorClientAttribute. + /// + /// Namespace where to generate attribute. + /// The representing the ActorMethodAttribute. + /// Throws when destinationNamespace is null. + public static SourceText GenerateActorClientAttributeSourceText(string destinationNamespace) + { + if (destinationNamespace is null) { - if (destinationNamespace is null) - { - throw new ArgumentNullException(nameof(destinationNamespace)); - } + throw new ArgumentNullException(nameof(destinationNamespace)); + } - string source = $@" + string source = $@" // #nullable enable @@ -77,11 +77,10 @@ internal sealed class {Constants.GenerateActorClientAttributeTypeName} : Attribu }} }}"; - return SourceText.From( - SyntaxFactory.ParseCompilationUnit(source) - .NormalizeWhitespace() - .ToFullString(), - Encoding.UTF8); - } + return SourceText.From( + SyntaxFactory.ParseCompilationUnit(source) + .NormalizeWhitespace() + .ToFullString(), + Encoding.UTF8); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/ActorId.cs b/src/Dapr.Actors/ActorId.cs index 88b10bb9c..0b3b558d3 100644 --- a/src/Dapr.Actors/ActorId.cs +++ b/src/Dapr.Actors/ActorId.cs @@ -11,164 +11,163 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors +namespace Dapr.Actors; + +using System; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using Dapr.Actors.Seralization; + +/// +/// The ActorId represents the identity of an actor within an actor service. +/// +[JsonConverter(typeof(ActorIdJsonConverter))] +[DataContract(Name = "ActorId")] +public class ActorId { - using System; - using System.Runtime.Serialization; - using System.Text.Json.Serialization; - using Dapr.Actors.Seralization; + [DataMember(Name = "ActorId", Order = 0, IsRequired = true)] + private readonly string stringId; /// - /// The ActorId represents the identity of an actor within an actor service. + /// Initializes a new instance of the class with id value of type . /// - [JsonConverter(typeof(ActorIdJsonConverter))] - [DataContract(Name = "ActorId")] - public class ActorId + /// Value for actor id. + public ActorId(string id) { - [DataMember(Name = "ActorId", Order = 0, IsRequired = true)] - private readonly string stringId; - - /// - /// Initializes a new instance of the class with id value of type . - /// - /// Value for actor id. - public ActorId(string id) + if (string.IsNullOrWhiteSpace(id)) { - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentException("The value cannot be null, empty or white spaces.", nameof(id)); - } - this.stringId = id; + throw new ArgumentException("The value cannot be null, empty or white spaces.", nameof(id)); } + this.stringId = id; + } - /// - /// Determines whether two specified actorIds have the same id. - /// - /// The first actorId to compare, or null. - /// The second actorId to compare, or null. - /// true if the id is same for both objects; otherwise, false. - public static bool operator ==(ActorId id1, ActorId id2) + /// + /// Determines whether two specified actorIds have the same id. + /// + /// The first actorId to compare, or null. + /// The second actorId to compare, or null. + /// true if the id is same for both objects; otherwise, false. + public static bool operator ==(ActorId id1, ActorId id2) + { + if (id1 is null && id2 is null) { - if (id1 is null && id2 is null) - { - return true; - } - else if (id1 is null || id2 is null) - { - return false; - } - else - { - return EqualsContents(id1, id2); - } + return true; } - - /// - /// Determines whether two specified actorIds have different values for id./>. - /// - /// The first actorId to compare, or null. - /// The second actorId to compare, or null. - /// true if the id is different for both objects; otherwise, true. - public static bool operator !=(ActorId id1, ActorId id2) + else if (id1 is null || id2 is null) { - return !(id1 == id2); + return false; } - - /// - /// Create a new instance of the with a random id value. - /// - /// A new ActorId object. - /// This method is thread-safe and generates a new random every time it is called. - public static ActorId CreateRandom() + else { - return new ActorId(Guid.NewGuid().ToString()); + return EqualsContents(id1, id2); } + } - /// - /// Gets id. - /// - /// The id value for ActorId. - public string GetId() - { - return this.stringId; - } + /// + /// Determines whether two specified actorIds have different values for id./>. + /// + /// The first actorId to compare, or null. + /// The second actorId to compare, or null. + /// true if the id is different for both objects; otherwise, true. + public static bool operator !=(ActorId id1, ActorId id2) + { + return !(id1 == id2); + } - /// - /// Overrides . - /// - /// Returns a string that represents the current object. - public override string ToString() - { - return this.stringId; - } + /// + /// Create a new instance of the with a random id value. + /// + /// A new ActorId object. + /// This method is thread-safe and generates a new random every time it is called. + public static ActorId CreateRandom() + { + return new ActorId(Guid.NewGuid().ToString()); + } - /// - /// Overrides . - /// - /// Hash code for the current object. - public override int GetHashCode() - { - return this.stringId.GetHashCode(); - } + /// + /// Gets id. + /// + /// The id value for ActorId. + public string GetId() + { + return this.stringId; + } - /// - /// Determines whether this instance and a specified object, which must also be a object, - /// have the same value. Overrides . - /// - /// The actorId to compare to this instance. - /// true if obj is a and its value is the same as this instance; - /// otherwise, false. If obj is null, the method returns false. - public override bool Equals(object obj) - { - if (obj is null || obj.GetType() != typeof(ActorId)) - { - return false; - } - else - { - return EqualsContents(this, (ActorId)obj); - } - } + /// + /// Overrides . + /// + /// Returns a string that represents the current object. + public override string ToString() + { + return this.stringId; + } - /// - /// Determines whether this instance and another specified object have the same value. - /// - /// The actorId to compare to this instance. - /// true if the id of the other parameter is the same as the id of this instance; otherwise, false. - /// If other is null, the method returns false. - public bool Equals(ActorId other) + /// + /// Overrides . + /// + /// Hash code for the current object. + public override int GetHashCode() + { + return this.stringId.GetHashCode(); + } + + /// + /// Determines whether this instance and a specified object, which must also be a object, + /// have the same value. Overrides . + /// + /// The actorId to compare to this instance. + /// true if obj is a and its value is the same as this instance; + /// otherwise, false. If obj is null, the method returns false. + public override bool Equals(object obj) + { + if (obj is null || obj.GetType() != typeof(ActorId)) { - if (other is null) - { - return false; - } - else - { - return EqualsContents(this, other); - } + return false; } - - /// - /// Compares this instance with a specified object and indicates whether this - /// instance precedes, follows, or appears in the same position in the sort order as the specified actorId. - /// - /// The actorId to compare with this instance. - /// A 32-bit signed integer that indicates whether this instance precedes, follows, or appears - /// in the same position in the sort order as the other parameter. - /// The comparison is done based on the id if both the instances. - public int CompareTo(ActorId other) + else { - return other is null ? 1 : CompareContents(this, other); + return EqualsContents(this, (ActorId)obj); } + } - private static bool EqualsContents(ActorId id1, ActorId id2) + /// + /// Determines whether this instance and another specified object have the same value. + /// + /// The actorId to compare to this instance. + /// true if the id of the other parameter is the same as the id of this instance; otherwise, false. + /// If other is null, the method returns false. + public bool Equals(ActorId other) + { + if (other is null) { - return string.Equals(id1.stringId, id2.stringId, StringComparison.OrdinalIgnoreCase); + return false; } - - private static int CompareContents(ActorId id1, ActorId id2) + else { - return string.Compare(id1.stringId, id2.stringId, StringComparison.OrdinalIgnoreCase); + return EqualsContents(this, other); } } -} + + /// + /// Compares this instance with a specified object and indicates whether this + /// instance precedes, follows, or appears in the same position in the sort order as the specified actorId. + /// + /// The actorId to compare with this instance. + /// A 32-bit signed integer that indicates whether this instance precedes, follows, or appears + /// in the same position in the sort order as the other parameter. + /// The comparison is done based on the id if both the instances. + public int CompareTo(ActorId other) + { + return other is null ? 1 : CompareContents(this, other); + } + + private static bool EqualsContents(ActorId id1, ActorId id2) + { + return string.Equals(id1.stringId, id2.stringId, StringComparison.OrdinalIgnoreCase); + } + + private static int CompareContents(ActorId id1, ActorId id2) + { + return string.Compare(id1.stringId, id2.stringId, StringComparison.OrdinalIgnoreCase); + } +} \ No newline at end of file diff --git a/src/Dapr.Actors/ActorMethodInvocationException.cs b/src/Dapr.Actors/ActorMethodInvocationException.cs index bbc3b5b71..3f0ba972b 100644 --- a/src/Dapr.Actors/ActorMethodInvocationException.cs +++ b/src/Dapr.Actors/ActorMethodInvocationException.cs @@ -11,44 +11,43 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors -{ - using System; +namespace Dapr.Actors; + +using System; +/// +/// Exception for Remote Actor Method Invocation. +/// +[Serializable] +public class ActorMethodInvocationException : DaprApiException +{ /// - /// Exception for Remote Actor Method Invocation. + /// Initializes a new instance of the class. /// - [Serializable] - public class ActorMethodInvocationException : DaprApiException + public ActorMethodInvocationException() + : base(Constants.ErrorActorInvokeMethod, false) { - /// - /// Initializes a new instance of the class. - /// - public ActorMethodInvocationException() - : base(Constants.ErrorActorInvokeMethod, false) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// True, if the exception is to be treated as an transient exception. - public ActorMethodInvocationException(string message, bool isTransient) - : base(message, Constants.ErrorActorInvokeMethod, isTransient) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// True, if the exception is to be treated as an transient exception. + public ActorMethodInvocationException(string message, bool isTransient) + : base(message, Constants.ErrorActorInvokeMethod, isTransient) + { + } - /// - /// Initializes a new instance of the class with a specified error - /// message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - /// True, if the exception is to be treated as an transient exception. - public ActorMethodInvocationException(string message, Exception innerException, bool isTransient) - : base(message, innerException, Constants.ErrorActorInvokeMethod, isTransient) - { - } + /// + /// Initializes a new instance of the class with a specified error + /// message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + /// True, if the exception is to be treated as an transient exception. + public ActorMethodInvocationException(string message, Exception innerException, bool isTransient) + : base(message, innerException, Constants.ErrorActorInvokeMethod, isTransient) + { } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/ActorReentrancyConfig.cs b/src/Dapr.Actors/ActorReentrancyConfig.cs index 2e65d1be1..317f13e66 100644 --- a/src/Dapr.Actors/ActorReentrancyConfig.cs +++ b/src/Dapr.Actors/ActorReentrancyConfig.cs @@ -11,49 +11,48 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors +namespace Dapr.Actors; + +/// +/// Represents the configuration required for Actor Reentrancy. +/// +/// See: https://docs.dapr.io/developing-applications/building-blocks/actors/actor-reentrancy/ +/// +public sealed class ActorReentrancyConfig { + private bool enabled; + private int? maxStackDepth; + /// - /// Represents the configuration required for Actor Reentrancy. - /// - /// See: https://docs.dapr.io/developing-applications/building-blocks/actors/actor-reentrancy/ + /// Determines if Actor Reentrancy is enabled or disabled. /// - public sealed class ActorReentrancyConfig + public bool Enabled { - private bool enabled; - private int? maxStackDepth; - - /// - /// Determines if Actor Reentrancy is enabled or disabled. - /// - public bool Enabled + get { - get - { - return this.enabled; - } + return this.enabled; + } - set - { - this.enabled = value; - } + set + { + this.enabled = value; } + } - /// - /// Optional parameter that will stop a reentrant call from progressing past the defined - /// limit. This is a safety measure against infinite reentrant calls. - /// - public int? MaxStackDepth + /// + /// Optional parameter that will stop a reentrant call from progressing past the defined + /// limit. This is a safety measure against infinite reentrant calls. + /// + public int? MaxStackDepth + { + get { - get - { - return this.maxStackDepth; - } + return this.maxStackDepth; + } - set - { - this.maxStackDepth = value; - } + set + { + this.maxStackDepth = value; } } } \ No newline at end of file diff --git a/src/Dapr.Actors/ActorReentrancyContextAccessor.cs b/src/Dapr.Actors/ActorReentrancyContextAccessor.cs index 9b92d6976..c4a776b3d 100644 --- a/src/Dapr.Actors/ActorReentrancyContextAccessor.cs +++ b/src/Dapr.Actors/ActorReentrancyContextAccessor.cs @@ -13,44 +13,43 @@ using System.Threading; -namespace Dapr.Actors +namespace Dapr.Actors; + +/// +/// Accessor for the reentrancy context. This provides the necessary ID to continue a reentrant request +/// across actor invocations. +/// +internal static class ActorReentrancyContextAccessor { + private static readonly AsyncLocal state = new AsyncLocal(); + /// - /// Accessor for the reentrancy context. This provides the necessary ID to continue a reentrant request - /// across actor invocations. + /// The reentrancy context for a given request, if one is present. /// - internal static class ActorReentrancyContextAccessor + public static string ReentrancyContext { - private static readonly AsyncLocal state = new AsyncLocal(); - - /// - /// The reentrancy context for a given request, if one is present. - /// - public static string ReentrancyContext + get { - get + return state.Value?.Context; + } + set + { + var holder = state.Value; + // Reset the current state if it exists. + if (holder != null) { - return state.Value?.Context; + holder.Context = null; } - set - { - var holder = state.Value; - // Reset the current state if it exists. - if (holder != null) - { - holder.Context = null; - } - if (value != null) - { - state.Value = new ActorReentrancyContextHolder { Context = value }; - } + if (value != null) + { + state.Value = new ActorReentrancyContextHolder { Context = value }; } } + } - private class ActorReentrancyContextHolder - { - public string Context; - } + private class ActorReentrancyContextHolder + { + public string Context; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/ActorReference.cs b/src/Dapr.Actors/ActorReference.cs index d72b6676f..96c1ca939 100644 --- a/src/Dapr.Actors/ActorReference.cs +++ b/src/Dapr.Actors/ActorReference.cs @@ -11,87 +11,86 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors -{ - using System; - using System.Runtime.Serialization; - using Dapr.Actors.Client; - using Dapr.Actors.Runtime; +namespace Dapr.Actors; + +using System; +using System.Runtime.Serialization; +using Dapr.Actors.Client; +using Dapr.Actors.Runtime; +/// +/// Encapsulation of a reference to an actor for serialization. +/// +[DataContract(Name = "ActorReference", Namespace = Constants.Namespace)] +[Serializable] +public sealed class ActorReference : IActorReference +{ /// - /// Encapsulation of a reference to an actor for serialization. + /// Initializes a new instance of the class. /// - [DataContract(Name = "ActorReference", Namespace = Constants.Namespace)] - [Serializable] - public sealed class ActorReference : IActorReference + public ActorReference() { - /// - /// Initializes a new instance of the class. - /// - public ActorReference() - { - } + } - /// - /// Gets or sets the of the actor. - /// - /// of the actor. - [DataMember(Name = "ActorId", Order = 0, IsRequired = true)] - public ActorId ActorId { get; set; } + /// + /// Gets or sets the of the actor. + /// + /// of the actor. + [DataMember(Name = "ActorId", Order = 0, IsRequired = true)] + public ActorId ActorId { get; set; } - /// - /// Gets or sets the implementation type of the actor. - /// - /// Implementation type name of the actor. - [DataMember(Name = "ActorType", Order = 0, IsRequired = true)] - public string ActorType { get; set; } + /// + /// Gets or sets the implementation type of the actor. + /// + /// Implementation type name of the actor. + [DataMember(Name = "ActorType", Order = 0, IsRequired = true)] + public string ActorType { get; set; } - /// - /// Gets for the actor. - /// - /// Actor object to get for. - /// object for the actor. - /// A null value is returned if actor is passed as null. - public static ActorReference Get(object actor) + /// + /// Gets for the actor. + /// + /// Actor object to get for. + /// object for the actor. + /// A null value is returned if actor is passed as null. + public static ActorReference Get(object actor) + { + if (actor != null) { - if (actor != null) - { - return GetActorReference(actor); - } - - return null; + return GetActorReference(actor); } - /// - public object Bind(Type actorInterfaceType) - { - return ActorProxy.DefaultProxyFactory.CreateActorProxy(this.ActorId, actorInterfaceType, this.ActorType); - } + return null; + } - private static ActorReference GetActorReference(object actor) - { - ArgumentNullException.ThrowIfNull(actor, nameof(actor)); + /// + public object Bind(Type actorInterfaceType) + { + return ActorProxy.DefaultProxyFactory.CreateActorProxy(this.ActorId, actorInterfaceType, this.ActorType); + } + + private static ActorReference GetActorReference(object actor) + { + ArgumentNullException.ThrowIfNull(actor, nameof(actor)); - var actorReference = actor switch + var actorReference = actor switch + { + // try as IActorProxy for backward compatibility as customers's mock framework may rely on it before V2 remoting stack. + IActorProxy actorProxy => new ActorReference() + { + ActorId = actorProxy.ActorId, + ActorType = actorProxy.ActorType, + }, + // Handle case when we want to get ActorReference inside the Actor implementation, + // we gather actor id and actor type from Actor base class. + Actor actorBase => new ActorReference() { - // try as IActorProxy for backward compatibility as customers's mock framework may rely on it before V2 remoting stack. - IActorProxy actorProxy => new ActorReference() - { - ActorId = actorProxy.ActorId, - ActorType = actorProxy.ActorType, - }, - // Handle case when we want to get ActorReference inside the Actor implementation, - // we gather actor id and actor type from Actor base class. - Actor actorBase => new ActorReference() - { - ActorId = actorBase.Id, - ActorType = actorBase.Host.ActorTypeInfo.ActorTypeName, - }, - // Handle case when we can't cast to IActorProxy or Actor. - _ => throw new ArgumentOutOfRangeException("actor", "Invalid actor object type."), - }; + ActorId = actorBase.Id, + ActorType = actorBase.Host.ActorTypeInfo.ActorTypeName, + }, + // Handle case when we can't cast to IActorProxy or Actor. + _ => throw new ArgumentOutOfRangeException("actor", "Invalid actor object type."), + }; - return actorReference; - } + return actorReference; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ActorCodeBuilder.cs b/src/Dapr.Actors/Builder/ActorCodeBuilder.cs index a025af7cd..2a08d3bfe 100644 --- a/src/Dapr.Actors/Builder/ActorCodeBuilder.cs +++ b/src/Dapr.Actors/Builder/ActorCodeBuilder.cs @@ -11,179 +11,178 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Generic; +using System.Linq; +using Dapr.Actors.Description; +using Dapr.Actors.Runtime; + +internal class ActorCodeBuilder : ICodeBuilder { - using System; - using System.Collections.Generic; - using System.Linq; - using Dapr.Actors.Description; - using Dapr.Actors.Runtime; + internal static readonly InterfaceDetailsStore InterfaceDetailsStore = new InterfaceDetailsStore(); + private static readonly ICodeBuilder Instance = new ActorCodeBuilder(new ActorCodeBuilderNames("V1")); + private static readonly object BuildLock = new object(); + private readonly MethodBodyTypesBuilder methodBodyTypesBuilder; + private readonly MethodDispatcherBuilder methodDispatcherBuilder; + private readonly ActorProxyGeneratorBuilder proxyGeneratorBuilder; - internal class ActorCodeBuilder : ICodeBuilder - { - internal static readonly InterfaceDetailsStore InterfaceDetailsStore = new InterfaceDetailsStore(); - private static readonly ICodeBuilder Instance = new ActorCodeBuilder(new ActorCodeBuilderNames("V1")); - private static readonly object BuildLock = new object(); - private readonly MethodBodyTypesBuilder methodBodyTypesBuilder; - private readonly MethodDispatcherBuilder methodDispatcherBuilder; - private readonly ActorProxyGeneratorBuilder proxyGeneratorBuilder; + private readonly Dictionary methodBodyTypesBuildResultMap; + private readonly Dictionary methodDispatcherBuildResultMap; + private readonly Dictionary proxyGeneratorBuildResultMap; - private readonly Dictionary methodBodyTypesBuildResultMap; - private readonly Dictionary methodDispatcherBuildResultMap; - private readonly Dictionary proxyGeneratorBuildResultMap; + private readonly ICodeBuilderNames codeBuilderNames; - private readonly ICodeBuilderNames codeBuilderNames; + public ActorCodeBuilder(ICodeBuilderNames codeBuilderNames) + { + this.codeBuilderNames = codeBuilderNames; - public ActorCodeBuilder(ICodeBuilderNames codeBuilderNames) - { - this.codeBuilderNames = codeBuilderNames; + this.methodBodyTypesBuildResultMap = new Dictionary(); + this.methodDispatcherBuildResultMap = new Dictionary(); + this.proxyGeneratorBuildResultMap = new Dictionary(); - this.methodBodyTypesBuildResultMap = new Dictionary(); - this.methodDispatcherBuildResultMap = new Dictionary(); - this.proxyGeneratorBuildResultMap = new Dictionary(); + this.methodBodyTypesBuilder = new MethodBodyTypesBuilder(this); + this.methodDispatcherBuilder = new MethodDispatcherBuilder(this); + this.proxyGeneratorBuilder = new ActorProxyGeneratorBuilder(this); + } - this.methodBodyTypesBuilder = new MethodBodyTypesBuilder(this); - this.methodDispatcherBuilder = new MethodDispatcherBuilder(this); - this.proxyGeneratorBuilder = new ActorProxyGeneratorBuilder(this); - } + ICodeBuilderNames ICodeBuilder.Names + { + get { return this.codeBuilderNames; } + } - ICodeBuilderNames ICodeBuilder.Names + public static ActorProxyGenerator GetOrCreateProxyGenerator(Type actorInterfaceType) + { + lock (BuildLock) { - get { return this.codeBuilderNames; } + return (ActorProxyGenerator)Instance.GetOrBuildProxyGenerator(actorInterfaceType).ProxyGenerator; } + } - public static ActorProxyGenerator GetOrCreateProxyGenerator(Type actorInterfaceType) + public static ActorMethodDispatcherBase GetOrCreateMethodDispatcher(Type actorInterfaceType) + { + lock (BuildLock) { - lock (BuildLock) - { - return (ActorProxyGenerator)Instance.GetOrBuildProxyGenerator(actorInterfaceType).ProxyGenerator; - } + return (ActorMethodDispatcherBase)Instance.GetOrBuilderMethodDispatcher(actorInterfaceType).MethodDispatcher; } + } - public static ActorMethodDispatcherBase GetOrCreateMethodDispatcher(Type actorInterfaceType) + MethodDispatcherBuildResult ICodeBuilder.GetOrBuilderMethodDispatcher(Type interfaceType) + { + if (this.TryGetMethodDispatcher(interfaceType, out var result)) { - lock (BuildLock) - { - return (ActorMethodDispatcherBase)Instance.GetOrBuilderMethodDispatcher(actorInterfaceType).MethodDispatcher; - } + return result; } - MethodDispatcherBuildResult ICodeBuilder.GetOrBuilderMethodDispatcher(Type interfaceType) - { - if (this.TryGetMethodDispatcher(interfaceType, out var result)) - { - return result; - } + result = this.BuildMethodDispatcher(interfaceType); + this.UpdateMethodDispatcherBuildMap(interfaceType, result); - result = this.BuildMethodDispatcher(interfaceType); - this.UpdateMethodDispatcherBuildMap(interfaceType, result); + return result; + } + MethodBodyTypesBuildResult ICodeBuilder.GetOrBuildMethodBodyTypes(Type interfaceType) + { + if (this.methodBodyTypesBuildResultMap.TryGetValue(interfaceType, out var result)) + { return result; } - MethodBodyTypesBuildResult ICodeBuilder.GetOrBuildMethodBodyTypes(Type interfaceType) - { - if (this.methodBodyTypesBuildResultMap.TryGetValue(interfaceType, out var result)) - { - return result; - } + result = this.BuildMethodBodyTypes(interfaceType); + this.methodBodyTypesBuildResultMap.Add(interfaceType, result); - result = this.BuildMethodBodyTypes(interfaceType); - this.methodBodyTypesBuildResultMap.Add(interfaceType, result); + return result; + } + ActorProxyGeneratorBuildResult ICodeBuilder.GetOrBuildProxyGenerator(Type interfaceType) + { + if (this.TryGetProxyGenerator(interfaceType, out var result)) + { return result; } - ActorProxyGeneratorBuildResult ICodeBuilder.GetOrBuildProxyGenerator(Type interfaceType) - { - if (this.TryGetProxyGenerator(interfaceType, out var result)) - { - return result; - } - - result = this.BuildProxyGenerator(interfaceType); - this.UpdateProxyGeneratorMap(interfaceType, result); + result = this.BuildProxyGenerator(interfaceType); + this.UpdateProxyGeneratorMap(interfaceType, result); - return result; - } + return result; + } - internal static bool TryGetKnownTypes(int interfaceId, out InterfaceDetails interfaceDetails) - { - return InterfaceDetailsStore.TryGetKnownTypes(interfaceId, out interfaceDetails); - } + internal static bool TryGetKnownTypes(int interfaceId, out InterfaceDetails interfaceDetails) + { + return InterfaceDetailsStore.TryGetKnownTypes(interfaceId, out interfaceDetails); + } - internal static bool TryGetKnownTypes(string interfaceName, out InterfaceDetails interfaceDetails) - { - return InterfaceDetailsStore.TryGetKnownTypes(interfaceName, out interfaceDetails); - } + internal static bool TryGetKnownTypes(string interfaceName, out InterfaceDetails interfaceDetails) + { + return InterfaceDetailsStore.TryGetKnownTypes(interfaceName, out interfaceDetails); + } - protected MethodDispatcherBuildResult BuildMethodDispatcher(Type interfaceType) - { - var actorInterfaceDescription = ActorInterfaceDescription.CreateUsingCRCId(interfaceType); - var res = this.methodDispatcherBuilder.Build(actorInterfaceDescription); - return res; - } + protected MethodDispatcherBuildResult BuildMethodDispatcher(Type interfaceType) + { + var actorInterfaceDescription = ActorInterfaceDescription.CreateUsingCRCId(interfaceType); + var res = this.methodDispatcherBuilder.Build(actorInterfaceDescription); + return res; + } - protected MethodBodyTypesBuildResult BuildMethodBodyTypes(Type interfaceType) - { - var actorInterfaceDescriptions = ActorInterfaceDescription.CreateUsingCRCId(interfaceType); - var result = this.methodBodyTypesBuilder.Build(actorInterfaceDescriptions); - InterfaceDetailsStore.UpdateKnownTypeDetail(actorInterfaceDescriptions, result); - return result; - } + protected MethodBodyTypesBuildResult BuildMethodBodyTypes(Type interfaceType) + { + var actorInterfaceDescriptions = ActorInterfaceDescription.CreateUsingCRCId(interfaceType); + var result = this.methodBodyTypesBuilder.Build(actorInterfaceDescriptions); + InterfaceDetailsStore.UpdateKnownTypeDetail(actorInterfaceDescriptions, result); + return result; + } - protected ActorProxyGeneratorBuildResult BuildProxyGenerator(Type interfaceType) - { - // create all actor interfaces that this interface derives from - var actorInterfaces = new List() { interfaceType }; - actorInterfaces.AddRange(interfaceType.GetActorInterfaces()); + protected ActorProxyGeneratorBuildResult BuildProxyGenerator(Type interfaceType) + { + // create all actor interfaces that this interface derives from + var actorInterfaces = new List() { interfaceType }; + actorInterfaces.AddRange(interfaceType.GetActorInterfaces()); - // create interface descriptions for all interfaces - var actorInterfaceDescriptions = actorInterfaces.Select( - t => ActorInterfaceDescription.CreateUsingCRCId(t)); + // create interface descriptions for all interfaces + var actorInterfaceDescriptions = actorInterfaces.Select( + t => ActorInterfaceDescription.CreateUsingCRCId(t)); - var res = this.proxyGeneratorBuilder.Build(interfaceType, actorInterfaceDescriptions); - return res; - } + var res = this.proxyGeneratorBuilder.Build(interfaceType, actorInterfaceDescriptions); + return res; + } - protected void UpdateMethodDispatcherBuildMap(Type interfaceType, MethodDispatcherBuildResult result) - { - this.methodDispatcherBuildResultMap.Add(interfaceType, result); - } + protected void UpdateMethodDispatcherBuildMap(Type interfaceType, MethodDispatcherBuildResult result) + { + this.methodDispatcherBuildResultMap.Add(interfaceType, result); + } - protected bool TryGetMethodDispatcher( - Type interfaceType, - out MethodDispatcherBuildResult builderMethodDispatcher) + protected bool TryGetMethodDispatcher( + Type interfaceType, + out MethodDispatcherBuildResult builderMethodDispatcher) + { + if (this.methodDispatcherBuildResultMap.TryGetValue(interfaceType, out var result)) { - if (this.methodDispatcherBuildResultMap.TryGetValue(interfaceType, out var result)) { - { - builderMethodDispatcher = result; - return true; - } + builderMethodDispatcher = result; + return true; } - - builderMethodDispatcher = null; - return false; } - protected void UpdateProxyGeneratorMap(Type interfaceType, ActorProxyGeneratorBuildResult result) - { - this.proxyGeneratorBuildResultMap.Add(interfaceType, result); - } + builderMethodDispatcher = null; + return false; + } - protected bool TryGetProxyGenerator(Type interfaceType, out ActorProxyGeneratorBuildResult orBuildProxyGenerator) + protected void UpdateProxyGeneratorMap(Type interfaceType, ActorProxyGeneratorBuildResult result) + { + this.proxyGeneratorBuildResultMap.Add(interfaceType, result); + } + + protected bool TryGetProxyGenerator(Type interfaceType, out ActorProxyGeneratorBuildResult orBuildProxyGenerator) + { + if (this.proxyGeneratorBuildResultMap.TryGetValue(interfaceType, out var result)) { - if (this.proxyGeneratorBuildResultMap.TryGetValue(interfaceType, out var result)) { - { - orBuildProxyGenerator = result; - return true; - } + orBuildProxyGenerator = result; + return true; } - - orBuildProxyGenerator = null; - return false; } + + orBuildProxyGenerator = null; + return false; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ActorCodeBuilderNames.cs b/src/Dapr.Actors/Builder/ActorCodeBuilderNames.cs index 5bf2ffd23..cbc38e76e 100644 --- a/src/Dapr.Actors/Builder/ActorCodeBuilderNames.cs +++ b/src/Dapr.Actors/Builder/ActorCodeBuilderNames.cs @@ -11,103 +11,102 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Globalization; + +internal class ActorCodeBuilderNames : ICodeBuilderNames { - using System; - using System.Globalization; - - internal class ActorCodeBuilderNames : ICodeBuilderNames - { - private readonly string namePrefix; - - public ActorCodeBuilderNames() - : this("actor") - { - } - - public ActorCodeBuilderNames(string namePrefix) - { - this.namePrefix = "actor" + namePrefix; - } - - public string InterfaceId - { - get { return "interfaceId"; } - } - - public string MethodId - { - get { return "methodId"; } - } - - public string RetVal - { - get { return "retVal"; } - } - - public string RequestBody - { - get { return "requestBody"; } - } - - public string GetMethodBodyTypesAssemblyName(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.mt", interfaceType.FullName, this.namePrefix); - } - - public string GetMethodBodyTypesAssemblyNamespace(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.mt", interfaceType.FullName, this.namePrefix); - } - - public string GetRequestBodyTypeName(string methodName) - { - return string.Format(CultureInfo.InvariantCulture, "{0}ReqBody", methodName); - } - - public string GetResponseBodyTypeName(string methodName) - { - return string.Format(CultureInfo.InvariantCulture, "{0}RespBody", methodName); - } - - public string GetMethodDispatcherAssemblyName(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.disp", interfaceType.FullName, this.namePrefix); - } - - public string GetMethodDispatcherAssemblyNamespace(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.disp", interfaceType.FullName, this.namePrefix); - } - - public string GetMethodDispatcherClassName(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}MethodDispatcher", interfaceType.Name); - } - - public string GetProxyAssemblyName(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.proxy", interfaceType.FullName, this.namePrefix); - } - - public string GetProxyAssemblyNamespace(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.proxy", interfaceType.FullName, this.namePrefix); - } - - public string GetProxyClassName(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}{1}Proxy", interfaceType.Name, this.namePrefix); - } - - public string GetProxyActivatorClassName(Type interfaceType) - { - return string.Format(CultureInfo.InvariantCulture, "{0}{1}ProxyActivator", interfaceType.Name, this.namePrefix); - } - - public string GetDataContractNamespace() - { - return Constants.Namespace; - } - } -} + private readonly string namePrefix; + + public ActorCodeBuilderNames() + : this("actor") + { + } + + public ActorCodeBuilderNames(string namePrefix) + { + this.namePrefix = "actor" + namePrefix; + } + + public string InterfaceId + { + get { return "interfaceId"; } + } + + public string MethodId + { + get { return "methodId"; } + } + + public string RetVal + { + get { return "retVal"; } + } + + public string RequestBody + { + get { return "requestBody"; } + } + + public string GetMethodBodyTypesAssemblyName(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.mt", interfaceType.FullName, this.namePrefix); + } + + public string GetMethodBodyTypesAssemblyNamespace(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.mt", interfaceType.FullName, this.namePrefix); + } + + public string GetRequestBodyTypeName(string methodName) + { + return string.Format(CultureInfo.InvariantCulture, "{0}ReqBody", methodName); + } + + public string GetResponseBodyTypeName(string methodName) + { + return string.Format(CultureInfo.InvariantCulture, "{0}RespBody", methodName); + } + + public string GetMethodDispatcherAssemblyName(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.disp", interfaceType.FullName, this.namePrefix); + } + + public string GetMethodDispatcherAssemblyNamespace(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.disp", interfaceType.FullName, this.namePrefix); + } + + public string GetMethodDispatcherClassName(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}MethodDispatcher", interfaceType.Name); + } + + public string GetProxyAssemblyName(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.proxy", interfaceType.FullName, this.namePrefix); + } + + public string GetProxyAssemblyNamespace(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.proxy", interfaceType.FullName, this.namePrefix); + } + + public string GetProxyClassName(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}{1}Proxy", interfaceType.Name, this.namePrefix); + } + + public string GetProxyActivatorClassName(Type interfaceType) + { + return string.Format(CultureInfo.InvariantCulture, "{0}{1}ProxyActivator", interfaceType.Name, this.namePrefix); + } + + public string GetDataContractNamespace() + { + return Constants.Namespace; + } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ActorMethodDispatcherBase.cs b/src/Dapr.Actors/Builder/ActorMethodDispatcherBase.cs index 6a8c19569..8eb5237c3 100644 --- a/src/Dapr.Actors/Builder/ActorMethodDispatcherBase.cs +++ b/src/Dapr.Actors/Builder/ActorMethodDispatcherBase.cs @@ -11,236 +11,235 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Communication; +using Dapr.Actors.Description; +using Dapr.Actors.Resources; + +/// +/// The class is used by actor remoting code generator to generate a type that dispatches requests to actor +/// object by invoking right method on it. +/// +public abstract class ActorMethodDispatcherBase { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Communication; - using Dapr.Actors.Description; - using Dapr.Actors.Resources; + private IReadOnlyDictionary methodNameMap; /// - /// The class is used by actor remoting code generator to generate a type that dispatches requests to actor - /// object by invoking right method on it. + /// Gets the id of the interface supported by this method dispatcher. /// - public abstract class ActorMethodDispatcherBase - { - private IReadOnlyDictionary methodNameMap; - - /// - /// Gets the id of the interface supported by this method dispatcher. - /// - public int InterfaceId { get; private set; } - - /// - /// Why we pass IMessageBodyFactory to this function instead of - /// setting at class level?. Since we cache MethodDispatcher for each interface, - /// we can't set IMessageBodyFactory at class level. - /// These can be cases where multiple IMessageBodyFactory implmenetation but single dispatcher class. - /// This method is used to dispatch request to the specified methodId of the - /// interface implemented by the remoted object. - /// - /// The object impplemented the remoted interface. - /// Id of the method to which to dispatch the request to. - /// The body of the request object that needs to be dispatched to the object. - /// IMessageBodyFactory implementaion. - /// The cancellation token that will be signaled if this operation is cancelled. - /// A task that represents the outstanding asynchronous call to the implementation object. - /// The return value of the task contains the returned value from the invoked method. - public Task DispatchAsync( - object objectImplementation, - int methodId, - IActorRequestMessageBody requestBody, - IActorMessageBodyFactory remotingMessageBodyFactory, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + public int InterfaceId { get; private set; } - var dispatchTask = this.OnDispatchAsync( - methodId, - objectImplementation, - requestBody, - remotingMessageBodyFactory, - cancellationToken); + /// + /// Why we pass IMessageBodyFactory to this function instead of + /// setting at class level?. Since we cache MethodDispatcher for each interface, + /// we can't set IMessageBodyFactory at class level. + /// These can be cases where multiple IMessageBodyFactory implmenetation but single dispatcher class. + /// This method is used to dispatch request to the specified methodId of the + /// interface implemented by the remoted object. + /// + /// The object impplemented the remoted interface. + /// Id of the method to which to dispatch the request to. + /// The body of the request object that needs to be dispatched to the object. + /// IMessageBodyFactory implementaion. + /// The cancellation token that will be signaled if this operation is cancelled. + /// A task that represents the outstanding asynchronous call to the implementation object. + /// The return value of the task contains the returned value from the invoked method. + public Task DispatchAsync( + object objectImplementation, + int methodId, + IActorRequestMessageBody requestBody, + IActorMessageBodyFactory remotingMessageBodyFactory, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - return dispatchTask; - } + var dispatchTask = this.OnDispatchAsync( + methodId, + objectImplementation, + requestBody, + remotingMessageBodyFactory, + cancellationToken); - /// - /// This method is used to dispatch one way messages to the specified methodId of the - /// interface implemented by the remoted object. - /// - /// The object implemented the remoted interface. - /// Id of the method to which to dispatch the request to. - /// The body of the request object that needs to be dispatched to the remoting implementation. - public void Dispatch(object objectImplementation, int methodId, IActorRequestMessageBody requestMessageBody) - { - this.OnDispatch(methodId, objectImplementation, requestMessageBody); - } + return dispatchTask; + } - /// - /// Gets the name of the method that has the specified methodId. - /// - /// The id of the method. - /// The name of the method corresponding to the specified method id. - public string GetMethodName(int methodId) - { - if (!this.methodNameMap.TryGetValue(methodId, out var methodName)) - { - throw new MissingMethodException(string.Format( - CultureInfo.CurrentCulture, - SR.ErrorMissingMethod, - methodId, - this.InterfaceId)); - } - - return methodName; - } + /// + /// This method is used to dispatch one way messages to the specified methodId of the + /// interface implemented by the remoted object. + /// + /// The object implemented the remoted interface. + /// Id of the method to which to dispatch the request to. + /// The body of the request object that needs to be dispatched to the remoting implementation. + public void Dispatch(object objectImplementation, int methodId, IActorRequestMessageBody requestMessageBody) + { + this.OnDispatch(methodId, objectImplementation, requestMessageBody); + } - internal void Initialize(InterfaceDescription description, IReadOnlyDictionary methodMap) + /// + /// Gets the name of the method that has the specified methodId. + /// + /// The id of the method. + /// The name of the method corresponding to the specified method id. + public string GetMethodName(int methodId) + { + if (!this.methodNameMap.TryGetValue(methodId, out var methodName)) { - this.SetInterfaceId(description.Id); - this.SetMethodNameMap(methodMap); + throw new MissingMethodException(string.Format( + CultureInfo.CurrentCulture, + SR.ErrorMissingMethod, + methodId, + this.InterfaceId)); } - internal void SetInterfaceId(int interfaceId) - { - this.InterfaceId = interfaceId; - } + return methodName; + } - internal void SetMethodNameMap(IReadOnlyDictionary methodNameMap) - { - this.methodNameMap = methodNameMap; - } + internal void Initialize(InterfaceDescription description, IReadOnlyDictionary methodMap) + { + this.SetInterfaceId(description.Id); + this.SetMethodNameMap(methodMap); + } - /// - /// This method is used to create the remoting response from the specified return value. - /// - /// Interface Name of the remoting Interface. - /// Method Name of the remoting method. - /// MethodId of the remoting method. - /// MessageFactory for the remoting Interface. - /// Response returned by remoting method. - /// Actor Response Message Body. - protected IActorResponseMessageBody CreateResponseMessageBody( - string interfaceName, - string methodName, - int methodId, - IActorMessageBodyFactory remotingMessageBodyFactory, - object response) - { - var msg = remotingMessageBodyFactory.CreateResponseMessageBody( - interfaceName, - methodName, - this.CreateWrappedResponseBody(methodId, response)); + internal void SetInterfaceId(int interfaceId) + { + this.InterfaceId = interfaceId; + } - if (!(msg is WrappedMessage)) - { - msg.Set(response); - } + internal void SetMethodNameMap(IReadOnlyDictionary methodNameMap) + { + this.methodNameMap = methodNameMap; + } - return msg; - } + /// + /// This method is used to create the remoting response from the specified return value. + /// + /// Interface Name of the remoting Interface. + /// Method Name of the remoting method. + /// MethodId of the remoting method. + /// MessageFactory for the remoting Interface. + /// Response returned by remoting method. + /// Actor Response Message Body. + protected IActorResponseMessageBody CreateResponseMessageBody( + string interfaceName, + string methodName, + int methodId, + IActorMessageBodyFactory remotingMessageBodyFactory, + object response) + { + var msg = remotingMessageBodyFactory.CreateResponseMessageBody( + interfaceName, + methodName, + this.CreateWrappedResponseBody(methodId, response)); - /// - /// This method is implemented by the generated method dispatcher to dispatch request to the specified methodId of the - /// interface implemented by the remoted object. - /// - /// Id of the method. - /// The remoted object instance. - /// Request body. - /// Remoting Message Body Factory implementation needed for creating response object. - /// Cancellation token. - /// - /// A Task that represents outstanding operation. - /// The result of the task is the return value from the method. - /// - protected abstract Task OnDispatchAsync( - int methodId, - object remotedObject, - IActorRequestMessageBody requestBody, - IActorMessageBodyFactory remotingMessageBodyFactory, - CancellationToken cancellationToken); - - /// - /// This method is implemented by the generated method dispatcher to dispatch one way messages to the specified methodId of the - /// interface implemented by the remoted object. - /// - /// Id of the method. - /// The remoted object instance. - /// Request body. - protected abstract void OnDispatch(int methodId, object remotedObject, IActorRequestMessageBody requestBody); - - /// - /// Internal - used by Service remoting. - /// - /// Interface Name of the remoting Interface. - /// Method Name of the remoting method. - /// MethodId of the remoting method. - /// MessageFactory for the remoting Interface. - /// continuation task. - /// - /// A Task that represents outstanding operation. - /// - /// The response type for the remoting method. - protected Task ContinueWithResult( - string interfaceName, - string methodName, - int methodId, - IActorMessageBodyFactory remotingMessageBodyFactory, - Task task) + if (!(msg is WrappedMessage)) { - return task.ContinueWith( - t => this.CreateResponseMessageBody(interfaceName, methodName, methodId, remotingMessageBodyFactory, t.GetAwaiter().GetResult()), - TaskContinuationOptions.ExecuteSynchronously); + msg.Set(response); } - /// - /// Internal - used by remoting. - /// - /// continuation task. - /// - /// A Task that represents outstanding operation. - /// - protected Task ContinueWith(Task task) - { - return task.ContinueWith( - t => - { - t.GetAwaiter().GetResult(); - return null; - }, - TaskContinuationOptions.ExecuteSynchronously); - } + return msg; + } - /// Internal - used by remoting - /// - /// This checks if we are wrapping actor message body or not. - /// - /// Actor Request Message Body. - /// true or false. - protected bool CheckIfItsWrappedRequest(IActorRequestMessageBody requestMessageBody) - { - if (requestMessageBody is WrappedMessage) + /// + /// This method is implemented by the generated method dispatcher to dispatch request to the specified methodId of the + /// interface implemented by the remoted object. + /// + /// Id of the method. + /// The remoted object instance. + /// Request body. + /// Remoting Message Body Factory implementation needed for creating response object. + /// Cancellation token. + /// + /// A Task that represents outstanding operation. + /// The result of the task is the return value from the method. + /// + protected abstract Task OnDispatchAsync( + int methodId, + object remotedObject, + IActorRequestMessageBody requestBody, + IActorMessageBodyFactory remotingMessageBodyFactory, + CancellationToken cancellationToken); + + /// + /// This method is implemented by the generated method dispatcher to dispatch one way messages to the specified methodId of the + /// interface implemented by the remoted object. + /// + /// Id of the method. + /// The remoted object instance. + /// Request body. + protected abstract void OnDispatch(int methodId, object remotedObject, IActorRequestMessageBody requestBody); + + /// + /// Internal - used by Service remoting. + /// + /// Interface Name of the remoting Interface. + /// Method Name of the remoting method. + /// MethodId of the remoting method. + /// MessageFactory for the remoting Interface. + /// continuation task. + /// + /// A Task that represents outstanding operation. + /// + /// The response type for the remoting method. + protected Task ContinueWithResult( + string interfaceName, + string methodName, + int methodId, + IActorMessageBodyFactory remotingMessageBodyFactory, + Task task) + { + return task.ContinueWith( + t => this.CreateResponseMessageBody(interfaceName, methodName, methodId, remotingMessageBodyFactory, t.GetAwaiter().GetResult()), + TaskContinuationOptions.ExecuteSynchronously); + } + + /// + /// Internal - used by remoting. + /// + /// continuation task. + /// + /// A Task that represents outstanding operation. + /// + protected Task ContinueWith(Task task) + { + return task.ContinueWith( + t => { - return true; - } + t.GetAwaiter().GetResult(); + return null; + }, + TaskContinuationOptions.ExecuteSynchronously); + } - return false; + /// Internal - used by remoting + /// + /// This checks if we are wrapping actor message body or not. + /// + /// Actor Request Message Body. + /// true or false. + protected bool CheckIfItsWrappedRequest(IActorRequestMessageBody requestMessageBody) + { + if (requestMessageBody is WrappedMessage) + { + return true; } - /// - /// Creates Wrapped Response Object for a method. - /// - /// MethodId of the remoting method. - /// Response for a method. - /// Wrapped Ressponse object. - // Generated By Code-gen - protected abstract object CreateWrappedResponseBody( - int methodId, - object retVal); + return false; } -} + + /// + /// Creates Wrapped Response Object for a method. + /// + /// MethodId of the remoting method. + /// Response for a method. + /// Wrapped Ressponse object. + // Generated By Code-gen + protected abstract object CreateWrappedResponseBody( + int methodId, + object retVal); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ActorProxyGenerator.cs b/src/Dapr.Actors/Builder/ActorProxyGenerator.cs index 6b4ad8e21..8829ff956 100644 --- a/src/Dapr.Actors/Builder/ActorProxyGenerator.cs +++ b/src/Dapr.Actors/Builder/ActorProxyGenerator.cs @@ -11,28 +11,27 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using Dapr.Actors.Client; + +internal class ActorProxyGenerator { - using System; - using Dapr.Actors.Client; + private readonly IProxyActivator proxyActivator; - internal class ActorProxyGenerator + public ActorProxyGenerator( + Type proxyInterfaceType, + IProxyActivator proxyActivator) { - private readonly IProxyActivator proxyActivator; - - public ActorProxyGenerator( - Type proxyInterfaceType, - IProxyActivator proxyActivator) - { - this.proxyActivator = proxyActivator; - this.ProxyInterfaceType = proxyInterfaceType; - } + this.proxyActivator = proxyActivator; + this.ProxyInterfaceType = proxyInterfaceType; + } - public Type ProxyInterfaceType { get; } + public Type ProxyInterfaceType { get; } - public ActorProxy CreateActorProxy() - { - return (ActorProxy)this.proxyActivator.CreateInstance(); - } + public ActorProxy CreateActorProxy() + { + return (ActorProxy)this.proxyActivator.CreateInstance(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ActorProxyGeneratorBuildResult.cs b/src/Dapr.Actors/Builder/ActorProxyGeneratorBuildResult.cs index 965a08e6e..3bdf54cc5 100644 --- a/src/Dapr.Actors/Builder/ActorProxyGeneratorBuildResult.cs +++ b/src/Dapr.Actors/Builder/ActorProxyGeneratorBuildResult.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using System; +namespace Dapr.Actors.Builder; + +using System; - internal class ActorProxyGeneratorBuildResult : BuildResult +internal class ActorProxyGeneratorBuildResult : BuildResult +{ + public ActorProxyGeneratorBuildResult(CodeBuilderContext buildContext) + : base(buildContext) { - public ActorProxyGeneratorBuildResult(CodeBuilderContext buildContext) - : base(buildContext) - { - } + } - public Type ProxyType { get; set; } + public Type ProxyType { get; set; } - public Type ProxyActivatorType { get; set; } + public Type ProxyActivatorType { get; set; } - public ActorProxyGenerator ProxyGenerator { get; set; } - } -} + public ActorProxyGenerator ProxyGenerator { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ActorProxyGeneratorBuilder.cs b/src/Dapr.Actors/Builder/ActorProxyGeneratorBuilder.cs index a62607fc4..fc911c587 100644 --- a/src/Dapr.Actors/Builder/ActorProxyGeneratorBuilder.cs +++ b/src/Dapr.Actors/Builder/ActorProxyGeneratorBuilder.cs @@ -11,559 +11,558 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Client; +using Dapr.Actors.Communication; +using Dapr.Actors.Description; + +internal class ActorProxyGeneratorBuilder : CodeBuilderModule { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Reflection.Emit; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Client; - using Dapr.Actors.Communication; - using Dapr.Actors.Description; - - internal class ActorProxyGeneratorBuilder : CodeBuilderModule + private readonly Type proxyBaseType; + private readonly MethodInfo createMessage; + private readonly MethodInfo invokeAsyncMethodInfo; + private readonly MethodInfo invokeMethodInfo; + private readonly MethodInfo continueWithResultMethodInfo; + private readonly MethodInfo continueWithMethodInfo; + private readonly MethodInfo checkIfitsWrapped; + + public ActorProxyGeneratorBuilder(ICodeBuilder codeBuilder) + : base(codeBuilder) { - private readonly Type proxyBaseType; - private readonly MethodInfo createMessage; - private readonly MethodInfo invokeAsyncMethodInfo; - private readonly MethodInfo invokeMethodInfo; - private readonly MethodInfo continueWithResultMethodInfo; - private readonly MethodInfo continueWithMethodInfo; - private readonly MethodInfo checkIfitsWrapped; - - public ActorProxyGeneratorBuilder(ICodeBuilder codeBuilder) - : base(codeBuilder) - { - this.proxyBaseType = typeof(ActorProxy); - - // TODO Should this search change to BindingFlags.NonPublic - this.invokeAsyncMethodInfo = this.proxyBaseType.GetMethod( - "InvokeMethodAsync", - BindingFlags.Instance | BindingFlags.NonPublic, - null, - CallingConventions.Any, - new[] { typeof(int), typeof(int), typeof(string), typeof(IActorRequestMessageBody), typeof(CancellationToken) }, - null); - - this.checkIfitsWrapped = this.proxyBaseType.GetMethod( - "CheckIfItsWrappedRequest", - BindingFlags.Instance | BindingFlags.NonPublic, - null, - CallingConventions.Any, - new[] { typeof(IActorRequestMessageBody) }, - null); - - this.createMessage = this.proxyBaseType.GetMethod( - "CreateRequestMessageBody", - BindingFlags.Instance | BindingFlags.NonPublic, - null, - CallingConventions.Any, - new[] { typeof(string), typeof(string), typeof(int), typeof(object) }, - null); - - this.invokeMethodInfo = this.proxyBaseType.GetMethod( - "Invoke", - BindingFlags.Instance | BindingFlags.NonPublic, - null, - CallingConventions.Any, - new[] { typeof(int), typeof(int), typeof(IActorRequestMessageBody) }, - null); - - this.continueWithResultMethodInfo = this.proxyBaseType.GetMethod( - "ContinueWithResult", - BindingFlags.Instance | BindingFlags.NonPublic, - null, - CallingConventions.Any, - new[] { typeof(int), typeof(int), typeof(Task) }, - null); - - this.continueWithMethodInfo = this.proxyBaseType.GetMethod( - "ContinueWith", - BindingFlags.Instance | BindingFlags.NonPublic); - } + this.proxyBaseType = typeof(ActorProxy); + + // TODO Should this search change to BindingFlags.NonPublic + this.invokeAsyncMethodInfo = this.proxyBaseType.GetMethod( + "InvokeMethodAsync", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + CallingConventions.Any, + new[] { typeof(int), typeof(int), typeof(string), typeof(IActorRequestMessageBody), typeof(CancellationToken) }, + null); + + this.checkIfitsWrapped = this.proxyBaseType.GetMethod( + "CheckIfItsWrappedRequest", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + CallingConventions.Any, + new[] { typeof(IActorRequestMessageBody) }, + null); + + this.createMessage = this.proxyBaseType.GetMethod( + "CreateRequestMessageBody", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + CallingConventions.Any, + new[] { typeof(string), typeof(string), typeof(int), typeof(object) }, + null); + + this.invokeMethodInfo = this.proxyBaseType.GetMethod( + "Invoke", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + CallingConventions.Any, + new[] { typeof(int), typeof(int), typeof(IActorRequestMessageBody) }, + null); + + this.continueWithResultMethodInfo = this.proxyBaseType.GetMethod( + "ContinueWithResult", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + CallingConventions.Any, + new[] { typeof(int), typeof(int), typeof(Task) }, + null); + + this.continueWithMethodInfo = this.proxyBaseType.GetMethod( + "ContinueWith", + BindingFlags.Instance | BindingFlags.NonPublic); + } - protected Type ProxyBaseType => this.proxyBaseType; + protected Type ProxyBaseType => this.proxyBaseType; - public ActorProxyGeneratorBuildResult Build( - Type proxyInterfaceType, - IEnumerable interfaceDescriptions) - { - // create the context to build the proxy - var context = new CodeBuilderContext( - assemblyName: this.CodeBuilder.Names.GetProxyAssemblyName(proxyInterfaceType), - assemblyNamespace: this.CodeBuilder.Names.GetProxyAssemblyNamespace(proxyInterfaceType), - enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(proxyInterfaceType)); - var result = new ActorProxyGeneratorBuildResult(context); - - // ensure that method data types are built for each of the remote interfaces - var methodBodyTypesResultsMap = interfaceDescriptions.ToDictionary( - d => d, - d => this.CodeBuilder.GetOrBuildMethodBodyTypes(d.InterfaceType)); - - // build the proxy class that implements all of the interfaces explicitly - result.ProxyType = this.BuildProxyType(context, proxyInterfaceType, methodBodyTypesResultsMap); - - // build the activator type to create instances of the proxy - result.ProxyActivatorType = this.BuildProxyActivatorType(context, proxyInterfaceType, result.ProxyType); - - // build the proxy generator - result.ProxyGenerator = this.CreateProxyGenerator( - proxyInterfaceType, - result.ProxyActivatorType); - - context.Complete(); - return result; - } + public ActorProxyGeneratorBuildResult Build( + Type proxyInterfaceType, + IEnumerable interfaceDescriptions) + { + // create the context to build the proxy + var context = new CodeBuilderContext( + assemblyName: this.CodeBuilder.Names.GetProxyAssemblyName(proxyInterfaceType), + assemblyNamespace: this.CodeBuilder.Names.GetProxyAssemblyNamespace(proxyInterfaceType), + enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(proxyInterfaceType)); + var result = new ActorProxyGeneratorBuildResult(context); + + // ensure that method data types are built for each of the remote interfaces + var methodBodyTypesResultsMap = interfaceDescriptions.ToDictionary( + d => d, + d => this.CodeBuilder.GetOrBuildMethodBodyTypes(d.InterfaceType)); + + // build the proxy class that implements all of the interfaces explicitly + result.ProxyType = this.BuildProxyType(context, proxyInterfaceType, methodBodyTypesResultsMap); + + // build the activator type to create instances of the proxy + result.ProxyActivatorType = this.BuildProxyActivatorType(context, proxyInterfaceType, result.ProxyType); + + // build the proxy generator + result.ProxyGenerator = this.CreateProxyGenerator( + proxyInterfaceType, + result.ProxyActivatorType); + + context.Complete(); + return result; + } - internal static LocalBuilder CreateWrappedRequestBody( + internal static LocalBuilder CreateWrappedRequestBody( MethodDescription methodDescription, MethodBodyTypes methodBodyTypes, ILGenerator ilGen, ParameterInfo[] parameters) + { + var parameterLength = parameters.Length; + if (methodDescription.HasCancellationToken) + { + // Cancellation token is tracked locally and should not be serialized and sent + // as a part of the request body. + parameterLength -= 1; + } + + if (parameterLength == 0) { - var parameterLength = parameters.Length; + return null; + } + + LocalBuilder wrappedRequestBody = ilGen.DeclareLocal(methodBodyTypes.RequestBodyType); + var requestBodyCtor = methodBodyTypes.RequestBodyType.GetConstructor(Type.EmptyTypes); + + if (requestBodyCtor != null) + { + ilGen.Emit(OpCodes.Newobj, requestBodyCtor); + ilGen.Emit(OpCodes.Stloc, wrappedRequestBody); + + var argsLength = parameters.Length; if (methodDescription.HasCancellationToken) { // Cancellation token is tracked locally and should not be serialized and sent // as a part of the request body. - parameterLength -= 1; + argsLength -= 1; } - if (parameterLength == 0) + for (var i = 0; i < argsLength; i++) { - return null; - } - - LocalBuilder wrappedRequestBody = ilGen.DeclareLocal(methodBodyTypes.RequestBodyType); - var requestBodyCtor = methodBodyTypes.RequestBodyType.GetConstructor(Type.EmptyTypes); - - if (requestBodyCtor != null) - { - ilGen.Emit(OpCodes.Newobj, requestBodyCtor); - ilGen.Emit(OpCodes.Stloc, wrappedRequestBody); - - var argsLength = parameters.Length; - if (methodDescription.HasCancellationToken) - { - // Cancellation token is tracked locally and should not be serialized and sent - // as a part of the request body. - argsLength -= 1; - } - - for (var i = 0; i < argsLength; i++) - { - ilGen.Emit(OpCodes.Ldloc, wrappedRequestBody); - ilGen.Emit(OpCodes.Ldarg, i + 1); - ilGen.Emit(OpCodes.Stfld, methodBodyTypes.RequestBodyType.GetField(parameters[i].Name)); - } + ilGen.Emit(OpCodes.Ldloc, wrappedRequestBody); + ilGen.Emit(OpCodes.Ldarg, i + 1); + ilGen.Emit(OpCodes.Stfld, methodBodyTypes.RequestBodyType.GetField(parameters[i].Name)); } - - return wrappedRequestBody; } - internal void AddVoidMethodImplementation( - ILGenerator ilGen, - int interfaceDescriptionId, - MethodDescription methodDescription, - LocalBuilder wrappedRequestBody, - string interfaceName) - { - var interfaceMethod = methodDescription.MethodInfo; + return wrappedRequestBody; + } - var parameters = interfaceMethod.GetParameters(); + internal void AddVoidMethodImplementation( + ILGenerator ilGen, + int interfaceDescriptionId, + MethodDescription methodDescription, + LocalBuilder wrappedRequestBody, + string interfaceName) + { + var interfaceMethod = methodDescription.MethodInfo; - LocalBuilder requestBody = null; + var parameters = interfaceMethod.GetParameters(); - if (parameters.Length > 0) - { - // create IServiceRemotingRequestMessageBody message - requestBody = this.CreateRequestRemotingMessageBody( - methodDescription, - interfaceName, - ilGen, - parameters.Length, - wrappedRequestBody); + LocalBuilder requestBody = null; - // Check if requestMessage is not implementing WrappedMessage , then call SetParam - this.SetParameterIfNeeded(ilGen, requestBody, parameters.Length, parameters); - } + if (parameters.Length > 0) + { + // create IServiceRemotingRequestMessageBody message + requestBody = this.CreateRequestRemotingMessageBody( + methodDescription, + interfaceName, + ilGen, + parameters.Length, + wrappedRequestBody); - // call the base Invoke method - ilGen.Emit(OpCodes.Ldarg_0); // base - ilGen.Emit(OpCodes.Ldc_I4, interfaceDescriptionId); // interfaceId - ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId + // Check if requestMessage is not implementing WrappedMessage , then call SetParam + this.SetParameterIfNeeded(ilGen, requestBody, parameters.Length, parameters); + } - if (parameters.Length > 0) - { - ilGen.Emit(OpCodes.Ldloc, requestBody); - } - else - { - ilGen.Emit(OpCodes.Ldnull); - } + // call the base Invoke method + ilGen.Emit(OpCodes.Ldarg_0); // base + ilGen.Emit(OpCodes.Ldc_I4, interfaceDescriptionId); // interfaceId + ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId - ilGen.EmitCall(OpCodes.Call, this.invokeMethodInfo, null); + if (parameters.Length > 0) + { + ilGen.Emit(OpCodes.Ldloc, requestBody); } + else + { + ilGen.Emit(OpCodes.Ldnull); + } + + ilGen.EmitCall(OpCodes.Call, this.invokeMethodInfo, null); + } - protected void AddInterfaceImplementations( - TypeBuilder classBuilder, - IDictionary methodBodyTypesResultsMap) + protected void AddInterfaceImplementations( + TypeBuilder classBuilder, + IDictionary methodBodyTypesResultsMap) + { + foreach (var item in methodBodyTypesResultsMap) { - foreach (var item in methodBodyTypesResultsMap) + var interfaceDescription = item.Key; + var methodBodyTypesMap = item.Value.MethodBodyTypesMap; + + foreach (var methodDescription in interfaceDescription.Methods) { - var interfaceDescription = item.Key; - var methodBodyTypesMap = item.Value.MethodBodyTypesMap; + var methodBodyTypes = methodBodyTypesMap[methodDescription.Name]; - foreach (var methodDescription in interfaceDescription.Methods) + if (TypeUtility.IsTaskType(methodDescription.ReturnType)) + { + this.AddAsyncMethodImplementation( + classBuilder, + interfaceDescription.Id, + methodDescription, + methodBodyTypes, + interfaceDescription.InterfaceType.FullName); + } + else if (TypeUtility.IsVoidType(methodDescription.ReturnType)) { - var methodBodyTypes = methodBodyTypesMap[methodDescription.Name]; - - if (TypeUtility.IsTaskType(methodDescription.ReturnType)) - { - this.AddAsyncMethodImplementation( - classBuilder, - interfaceDescription.Id, - methodDescription, - methodBodyTypes, - interfaceDescription.InterfaceType.FullName); - } - else if (TypeUtility.IsVoidType(methodDescription.ReturnType)) - { - this.AddVoidMethodImplementation( - classBuilder, - interfaceDescription.Id, - methodDescription, - methodBodyTypes, - interfaceDescription.InterfaceType.FullName); - } + this.AddVoidMethodImplementation( + classBuilder, + interfaceDescription.Id, + methodDescription, + methodBodyTypes, + interfaceDescription.InterfaceType.FullName); } } } + } - protected ActorProxyGenerator CreateProxyGenerator( - Type proxyInterfaceType, - Type proxyActivatorType) - { - return new ActorProxyGenerator( - proxyInterfaceType, - (IProxyActivator)Activator.CreateInstance(proxyActivatorType)); - } + protected ActorProxyGenerator CreateProxyGenerator( + Type proxyInterfaceType, + Type proxyActivatorType) + { + return new ActorProxyGenerator( + proxyInterfaceType, + (IProxyActivator)Activator.CreateInstance(proxyActivatorType)); + } - private static void AddCreateInstanceMethod( - TypeBuilder classBuilder, - Type proxyType) + private static void AddCreateInstanceMethod( + TypeBuilder classBuilder, + Type proxyType) + { + var methodBuilder = CodeBuilderUtils.CreatePublicMethodBuilder( + classBuilder, + "CreateInstance", + typeof(IActorProxy)); + + var ilGen = methodBuilder.GetILGenerator(); + var proxyCtor = proxyType.GetConstructor(Type.EmptyTypes); + if (proxyCtor != null) { - var methodBuilder = CodeBuilderUtils.CreatePublicMethodBuilder( - classBuilder, - "CreateInstance", - typeof(IActorProxy)); - - var ilGen = methodBuilder.GetILGenerator(); - var proxyCtor = proxyType.GetConstructor(Type.EmptyTypes); - if (proxyCtor != null) - { - ilGen.Emit(OpCodes.Newobj, proxyCtor); - ilGen.Emit(OpCodes.Ret); - } - else - { - ilGen.Emit(OpCodes.Ldnull); - ilGen.Emit(OpCodes.Ret); - } + ilGen.Emit(OpCodes.Newobj, proxyCtor); + ilGen.Emit(OpCodes.Ret); } - - private Type BuildProxyActivatorType( - CodeBuilderContext context, - Type proxyInterfaceType, - Type proxyType) + else { - var classBuilder = CodeBuilderUtils.CreateClassBuilder( - context.ModuleBuilder, - ns: context.AssemblyNamespace, - className: this.CodeBuilder.Names.GetProxyActivatorClassName(proxyInterfaceType), - interfaces: new[] { typeof(IProxyActivator) }); - - AddCreateInstanceMethod(classBuilder, proxyType); - return classBuilder.CreateTypeInfo().AsType(); + ilGen.Emit(OpCodes.Ldnull); + ilGen.Emit(OpCodes.Ret); } + } - private void AddAsyncMethodImplementation( + private Type BuildProxyActivatorType( + CodeBuilderContext context, + Type proxyInterfaceType, + Type proxyType) + { + var classBuilder = CodeBuilderUtils.CreateClassBuilder( + context.ModuleBuilder, + ns: context.AssemblyNamespace, + className: this.CodeBuilder.Names.GetProxyActivatorClassName(proxyInterfaceType), + interfaces: new[] { typeof(IProxyActivator) }); + + AddCreateInstanceMethod(classBuilder, proxyType); + return classBuilder.CreateTypeInfo().AsType(); + } + + private void AddAsyncMethodImplementation( TypeBuilder classBuilder, int interfaceId, MethodDescription methodDescription, MethodBodyTypes methodBodyTypes, string interfaceName) + { + var interfaceMethod = methodDescription.MethodInfo; + var parameters = interfaceMethod.GetParameters(); + var methodBuilder = CodeBuilderUtils.CreateExplitInterfaceMethodBuilder( + classBuilder, + interfaceMethod); + var ilGen = methodBuilder.GetILGenerator(); + var parameterLength = parameters.Length; + if (methodDescription.HasCancellationToken) { - var interfaceMethod = methodDescription.MethodInfo; - var parameters = interfaceMethod.GetParameters(); - var methodBuilder = CodeBuilderUtils.CreateExplitInterfaceMethodBuilder( - classBuilder, - interfaceMethod); - var ilGen = methodBuilder.GetILGenerator(); - var parameterLength = parameters.Length; - if (methodDescription.HasCancellationToken) - { - // Cancellation token is tracked locally and should not be serialized and sent - // as a part of the request body. - parameterLength -= 1; - } - - LocalBuilder requestMessage = null; - if (parameterLength > 0) - { - // Create Wrapped Message - // create requestBody and assign the values to its field from the arguments - var wrappedRequestBody = CreateWrappedRequestBody(methodDescription, methodBodyTypes, ilGen, parameters); - - // create IServiceRemotingRequestMessageBody message - requestMessage = this.CreateRequestRemotingMessageBody(methodDescription, interfaceName, ilGen, parameterLength, wrappedRequestBody); + // Cancellation token is tracked locally and should not be serialized and sent + // as a part of the request body. + parameterLength -= 1; + } - // Check if requestMessage is not implementing WrappedMessage , then call SetParam - this.SetParameterIfNeeded(ilGen, requestMessage, parameterLength, parameters); - } + LocalBuilder requestMessage = null; + if (parameterLength > 0) + { + // Create Wrapped Message + // create requestBody and assign the values to its field from the arguments + var wrappedRequestBody = CreateWrappedRequestBody(methodDescription, methodBodyTypes, ilGen, parameters); - var objectTask = ilGen.DeclareLocal(typeof(Task)); + // create IServiceRemotingRequestMessageBody message + requestMessage = this.CreateRequestRemotingMessageBody(methodDescription, interfaceName, ilGen, parameterLength, wrappedRequestBody); - // call the base InvokeMethodAsync method - ilGen.Emit(OpCodes.Ldarg_0); // base - ilGen.Emit(OpCodes.Ldc_I4, interfaceId); // interfaceId - ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId - ilGen.Emit(OpCodes.Ldstr, methodDescription.Name); // method name - - if (requestMessage != null) - { - ilGen.Emit(OpCodes.Ldloc, requestMessage); - } - else - { - ilGen.Emit(OpCodes.Ldnull); - } + // Check if requestMessage is not implementing WrappedMessage , then call SetParam + this.SetParameterIfNeeded(ilGen, requestMessage, parameterLength, parameters); + } - // Cancellation token argument - if (methodDescription.HasCancellationToken) - { - // Last argument should be the cancellation token - var cancellationTokenArgIndex = parameters.Length; - ilGen.Emit(OpCodes.Ldarg, cancellationTokenArgIndex); - } - else - { - var cancellationTokenNone = typeof(CancellationToken).GetMethod("get_None"); - ilGen.EmitCall(OpCodes.Call, cancellationTokenNone, null); - } + var objectTask = ilGen.DeclareLocal(typeof(Task)); - ilGen.EmitCall(OpCodes.Call, this.invokeAsyncMethodInfo, null); - ilGen.Emit(OpCodes.Stloc, objectTask); + // call the base InvokeMethodAsync method + ilGen.Emit(OpCodes.Ldarg_0); // base + ilGen.Emit(OpCodes.Ldc_I4, interfaceId); // interfaceId + ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId + ilGen.Emit(OpCodes.Ldstr, methodDescription.Name); // method name - // call the base method to get the continuation task and - // convert the response body to return value when the task is finished - if (TypeUtility.IsTaskType(methodDescription.ReturnType) && - methodDescription.ReturnType.GetTypeInfo().IsGenericType) - { - var retvalType = methodDescription.ReturnType.GetGenericArguments()[0]; - - ilGen.Emit(OpCodes.Ldarg_0); // base pointer - ilGen.Emit(OpCodes.Ldc_I4, interfaceId); // interfaceId - ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId - ilGen.Emit(OpCodes.Ldloc, objectTask); // task - ilGen.Emit(OpCodes.Call, this.continueWithResultMethodInfo.MakeGenericMethod(retvalType)); - ilGen.Emit(OpCodes.Ret); // return base.ContinueWithResult(task); - } - else - { - ilGen.Emit(OpCodes.Ldarg_0); // base pointer - ilGen.Emit(OpCodes.Ldloc, objectTask); // task - ilGen.Emit(OpCodes.Call, this.continueWithMethodInfo); - ilGen.Emit(OpCodes.Ret); // return base.ContinueWith(task); - } + if (requestMessage != null) + { + ilGen.Emit(OpCodes.Ldloc, requestMessage); } - - private Type BuildProxyType( - CodeBuilderContext context, - Type proxyInterfaceType, - IDictionary methodBodyTypesResultsMap) + else { - var classBuilder = CodeBuilderUtils.CreateClassBuilder( - context.ModuleBuilder, - ns: context.AssemblyNamespace, - className: this.CodeBuilder.Names.GetProxyClassName(proxyInterfaceType), - baseType: this.proxyBaseType, - interfaces: methodBodyTypesResultsMap.Select(item => item.Key.InterfaceType).ToArray()); - - this.AddGetReturnValueMethod(classBuilder, methodBodyTypesResultsMap); - this.AddInterfaceImplementations(classBuilder, methodBodyTypesResultsMap); - - return classBuilder.CreateTypeInfo().AsType(); + ilGen.Emit(OpCodes.Ldnull); } - private void AddGetReturnValueMethod( - TypeBuilder classBuilder, - IDictionary methodBodyTypesResultsMap) + // Cancellation token argument + if (methodDescription.HasCancellationToken) + { + // Last argument should be the cancellation token + var cancellationTokenArgIndex = parameters.Length; + ilGen.Emit(OpCodes.Ldarg, cancellationTokenArgIndex); + } + else { - var methodBuilder = CodeBuilderUtils.CreateProtectedMethodBuilder( - classBuilder, - "GetReturnValue", - typeof(object), // return value from the reponseBody - typeof(int), // interfaceId - typeof(int), // methodId - typeof(object)); // responseBody + var cancellationTokenNone = typeof(CancellationToken).GetMethod("get_None"); + ilGen.EmitCall(OpCodes.Call, cancellationTokenNone, null); + } - var ilGen = methodBuilder.GetILGenerator(); + ilGen.EmitCall(OpCodes.Call, this.invokeAsyncMethodInfo, null); + ilGen.Emit(OpCodes.Stloc, objectTask); - foreach (var item in methodBodyTypesResultsMap) - { - var interfaceDescription = item.Key; - var methodBodyTypesMap = item.Value.MethodBodyTypesMap; + // call the base method to get the continuation task and + // convert the response body to return value when the task is finished + if (TypeUtility.IsTaskType(methodDescription.ReturnType) && + methodDescription.ReturnType.GetTypeInfo().IsGenericType) + { + var retvalType = methodDescription.ReturnType.GetGenericArguments()[0]; - foreach (var methodDescription in interfaceDescription.Methods) - { - var methodBodyTypes = methodBodyTypesMap[methodDescription.Name]; - if (methodBodyTypes.ResponseBodyType == null) - { - continue; - } + ilGen.Emit(OpCodes.Ldarg_0); // base pointer + ilGen.Emit(OpCodes.Ldc_I4, interfaceId); // interfaceId + ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId + ilGen.Emit(OpCodes.Ldloc, objectTask); // task + ilGen.Emit(OpCodes.Call, this.continueWithResultMethodInfo.MakeGenericMethod(retvalType)); + ilGen.Emit(OpCodes.Ret); // return base.ContinueWithResult(task); + } + else + { + ilGen.Emit(OpCodes.Ldarg_0); // base pointer + ilGen.Emit(OpCodes.Ldloc, objectTask); // task + ilGen.Emit(OpCodes.Call, this.continueWithMethodInfo); + ilGen.Emit(OpCodes.Ret); // return base.ContinueWith(task); + } + } - var elseLabel = ilGen.DefineLabel(); + private Type BuildProxyType( + CodeBuilderContext context, + Type proxyInterfaceType, + IDictionary methodBodyTypesResultsMap) + { + var classBuilder = CodeBuilderUtils.CreateClassBuilder( + context.ModuleBuilder, + ns: context.AssemblyNamespace, + className: this.CodeBuilder.Names.GetProxyClassName(proxyInterfaceType), + baseType: this.proxyBaseType, + interfaces: methodBodyTypesResultsMap.Select(item => item.Key.InterfaceType).ToArray()); - this.AddIfInterfaceIdAndMethodIdReturnRetvalBlock( - ilGen, - elseLabel, - interfaceDescription.Id, - methodDescription.Id, - methodBodyTypes.ResponseBodyType); + this.AddGetReturnValueMethod(classBuilder, methodBodyTypesResultsMap); + this.AddInterfaceImplementations(classBuilder, methodBodyTypesResultsMap); - ilGen.MarkLabel(elseLabel); - } - } + return classBuilder.CreateTypeInfo().AsType(); + } - // return null; (if method id's and interfaceId do not mGetReturnValueatch) - ilGen.Emit(OpCodes.Ldnull); - ilGen.Emit(OpCodes.Ret); - } + private void AddGetReturnValueMethod( + TypeBuilder classBuilder, + IDictionary methodBodyTypesResultsMap) + { + var methodBuilder = CodeBuilderUtils.CreateProtectedMethodBuilder( + classBuilder, + "GetReturnValue", + typeof(object), // return value from the reponseBody + typeof(int), // interfaceId + typeof(int), // methodId + typeof(object)); // responseBody + + var ilGen = methodBuilder.GetILGenerator(); - private void SetParameterIfNeeded( - ILGenerator ilGen, - LocalBuilder requestMessage, - int parameterLength, - ParameterInfo[] parameters) + foreach (var item in methodBodyTypesResultsMap) { - var boolres = ilGen.DeclareLocal(typeof(bool)); - var boolres2 = ilGen.DeclareLocal(typeof(bool)); - ilGen.Emit(OpCodes.Ldarg_0); // base - ilGen.Emit(OpCodes.Ldloc_1, requestMessage); - ilGen.Emit(OpCodes.Call, this.checkIfitsWrapped); - ilGen.Emit(OpCodes.Stloc, boolres); - ilGen.Emit(OpCodes.Ldloc_2); - ilGen.Emit(OpCodes.Ldc_I4_0); - ilGen.Emit(OpCodes.Ceq); - ilGen.Emit(OpCodes.Stloc, boolres2); - ilGen.Emit(OpCodes.Ldloc_3, boolres2); - var elseLabel = ilGen.DefineLabel(); - ilGen.Emit(OpCodes.Brfalse, elseLabel); - - // if false ,Call SetParamater - var setMethod = typeof(IActorRequestMessageBody).GetMethod("SetParameter"); - - // Add to Dictionary - for (var i = 0; i < parameterLength; i++) + var interfaceDescription = item.Key; + var methodBodyTypesMap = item.Value.MethodBodyTypesMap; + + foreach (var methodDescription in interfaceDescription.Methods) { - ilGen.Emit(OpCodes.Ldloc, requestMessage); - ilGen.Emit(OpCodes.Ldc_I4, i); - ilGen.Emit(OpCodes.Ldstr, parameters[i].Name); - ilGen.Emit(OpCodes.Ldarg, i + 1); - if (!parameters[i].ParameterType.IsClass) + var methodBodyTypes = methodBodyTypesMap[methodDescription.Name]; + if (methodBodyTypes.ResponseBodyType == null) { - ilGen.Emit(OpCodes.Box, parameters[i].ParameterType); + continue; } - ilGen.Emit(OpCodes.Callvirt, setMethod); - } + var elseLabel = ilGen.DefineLabel(); - ilGen.MarkLabel(elseLabel); - } + this.AddIfInterfaceIdAndMethodIdReturnRetvalBlock( + ilGen, + elseLabel, + interfaceDescription.Id, + methodDescription.Id, + methodBodyTypes.ResponseBodyType); - private LocalBuilder CreateRequestRemotingMessageBody( - MethodDescription methodDescription, - string interfaceName, - ILGenerator ilGen, - int parameterLength, - LocalBuilder wrappedRequestBody) - { - LocalBuilder requestMessage; - ilGen.Emit(OpCodes.Ldarg_0); // base - requestMessage = ilGen.DeclareLocal(typeof(IActorRequestMessageBody)); - ilGen.Emit(OpCodes.Ldstr, interfaceName); - ilGen.Emit(OpCodes.Ldstr, methodDescription.Name); - ilGen.Emit(OpCodes.Ldc_I4, parameterLength); - ilGen.Emit(OpCodes.Ldloc, wrappedRequestBody); - ilGen.EmitCall(OpCodes.Call, this.createMessage, null); - ilGen.Emit(OpCodes.Stloc, requestMessage); - return requestMessage; + ilGen.MarkLabel(elseLabel); + } } - private void AddIfInterfaceIdAndMethodIdReturnRetvalBlock( - ILGenerator ilGen, - Label elseLabel, - int interfaceId, - int methodId, - Type responseBodyType) + // return null; (if method id's and interfaceId do not mGetReturnValueatch) + ilGen.Emit(OpCodes.Ldnull); + ilGen.Emit(OpCodes.Ret); + } + + private void SetParameterIfNeeded( + ILGenerator ilGen, + LocalBuilder requestMessage, + int parameterLength, + ParameterInfo[] parameters) + { + var boolres = ilGen.DeclareLocal(typeof(bool)); + var boolres2 = ilGen.DeclareLocal(typeof(bool)); + ilGen.Emit(OpCodes.Ldarg_0); // base + ilGen.Emit(OpCodes.Ldloc_1, requestMessage); + ilGen.Emit(OpCodes.Call, this.checkIfitsWrapped); + ilGen.Emit(OpCodes.Stloc, boolres); + ilGen.Emit(OpCodes.Ldloc_2); + ilGen.Emit(OpCodes.Ldc_I4_0); + ilGen.Emit(OpCodes.Ceq); + ilGen.Emit(OpCodes.Stloc, boolres2); + ilGen.Emit(OpCodes.Ldloc_3, boolres2); + var elseLabel = ilGen.DefineLabel(); + ilGen.Emit(OpCodes.Brfalse, elseLabel); + + // if false ,Call SetParamater + var setMethod = typeof(IActorRequestMessageBody).GetMethod("SetParameter"); + + // Add to Dictionary + for (var i = 0; i < parameterLength; i++) { - // if (interfaceId == ) - ilGen.Emit(OpCodes.Ldarg_1); - ilGen.Emit(OpCodes.Ldc_I4, interfaceId); - ilGen.Emit(OpCodes.Bne_Un, elseLabel); - - // if (methodId == ) - ilGen.Emit(OpCodes.Ldarg_2); - ilGen.Emit(OpCodes.Ldc_I4, methodId); - ilGen.Emit(OpCodes.Bne_Un, elseLabel); - - var castedResponseBody = ilGen.DeclareLocal(responseBodyType); - ilGen.Emit(OpCodes.Ldarg_3); // load responseBody object - ilGen.Emit(OpCodes.Castclass, responseBodyType); // cast it to responseBodyType - ilGen.Emit(OpCodes.Stloc, castedResponseBody); // store casted result to castedResponseBody local variable - - var fieldInfo = responseBodyType.GetField(this.CodeBuilder.Names.RetVal); - ilGen.Emit(OpCodes.Ldloc, castedResponseBody); - ilGen.Emit(OpCodes.Ldfld, fieldInfo); - if (!fieldInfo.FieldType.GetTypeInfo().IsClass) + ilGen.Emit(OpCodes.Ldloc, requestMessage); + ilGen.Emit(OpCodes.Ldc_I4, i); + ilGen.Emit(OpCodes.Ldstr, parameters[i].Name); + ilGen.Emit(OpCodes.Ldarg, i + 1); + if (!parameters[i].ParameterType.IsClass) { - ilGen.Emit(OpCodes.Box, fieldInfo.FieldType); + ilGen.Emit(OpCodes.Box, parameters[i].ParameterType); } - ilGen.Emit(OpCodes.Ret); + ilGen.Emit(OpCodes.Callvirt, setMethod); } - private void AddVoidMethodImplementation( - TypeBuilder classBuilder, - int interfaceDescriptionId, - MethodDescription methodDescription, - MethodBodyTypes methodBodyTypes, - string interfaceName) + ilGen.MarkLabel(elseLabel); + } + + private LocalBuilder CreateRequestRemotingMessageBody( + MethodDescription methodDescription, + string interfaceName, + ILGenerator ilGen, + int parameterLength, + LocalBuilder wrappedRequestBody) + { + LocalBuilder requestMessage; + ilGen.Emit(OpCodes.Ldarg_0); // base + requestMessage = ilGen.DeclareLocal(typeof(IActorRequestMessageBody)); + ilGen.Emit(OpCodes.Ldstr, interfaceName); + ilGen.Emit(OpCodes.Ldstr, methodDescription.Name); + ilGen.Emit(OpCodes.Ldc_I4, parameterLength); + ilGen.Emit(OpCodes.Ldloc, wrappedRequestBody); + ilGen.EmitCall(OpCodes.Call, this.createMessage, null); + ilGen.Emit(OpCodes.Stloc, requestMessage); + return requestMessage; + } + + private void AddIfInterfaceIdAndMethodIdReturnRetvalBlock( + ILGenerator ilGen, + Label elseLabel, + int interfaceId, + int methodId, + Type responseBodyType) + { + // if (interfaceId == ) + ilGen.Emit(OpCodes.Ldarg_1); + ilGen.Emit(OpCodes.Ldc_I4, interfaceId); + ilGen.Emit(OpCodes.Bne_Un, elseLabel); + + // if (methodId == ) + ilGen.Emit(OpCodes.Ldarg_2); + ilGen.Emit(OpCodes.Ldc_I4, methodId); + ilGen.Emit(OpCodes.Bne_Un, elseLabel); + + var castedResponseBody = ilGen.DeclareLocal(responseBodyType); + ilGen.Emit(OpCodes.Ldarg_3); // load responseBody object + ilGen.Emit(OpCodes.Castclass, responseBodyType); // cast it to responseBodyType + ilGen.Emit(OpCodes.Stloc, castedResponseBody); // store casted result to castedResponseBody local variable + + var fieldInfo = responseBodyType.GetField(this.CodeBuilder.Names.RetVal); + ilGen.Emit(OpCodes.Ldloc, castedResponseBody); + ilGen.Emit(OpCodes.Ldfld, fieldInfo); + if (!fieldInfo.FieldType.GetTypeInfo().IsClass) { - var interfaceMethod = methodDescription.MethodInfo; + ilGen.Emit(OpCodes.Box, fieldInfo.FieldType); + } - var methodBuilder = CodeBuilderUtils.CreateExplitInterfaceMethodBuilder( - classBuilder, - interfaceMethod); + ilGen.Emit(OpCodes.Ret); + } - var ilGen = methodBuilder.GetILGenerator(); + private void AddVoidMethodImplementation( + TypeBuilder classBuilder, + int interfaceDescriptionId, + MethodDescription methodDescription, + MethodBodyTypes methodBodyTypes, + string interfaceName) + { + var interfaceMethod = methodDescription.MethodInfo; - // Create Wrapped Request - LocalBuilder wrappedRequestBody = - CreateWrappedRequestBody(methodDescription, methodBodyTypes, ilGen, methodDescription.MethodInfo.GetParameters()); + var methodBuilder = CodeBuilderUtils.CreateExplitInterfaceMethodBuilder( + classBuilder, + interfaceMethod); - this.AddVoidMethodImplementation( - ilGen, - interfaceDescriptionId, - methodDescription, - wrappedRequestBody, - interfaceName); + var ilGen = methodBuilder.GetILGenerator(); - ilGen.Emit(OpCodes.Ret); - } + // Create Wrapped Request + LocalBuilder wrappedRequestBody = + CreateWrappedRequestBody(methodDescription, methodBodyTypes, ilGen, methodDescription.MethodInfo.GetParameters()); + + this.AddVoidMethodImplementation( + ilGen, + interfaceDescriptionId, + methodDescription, + wrappedRequestBody, + interfaceName); + + ilGen.Emit(OpCodes.Ret); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/BuildResult.cs b/src/Dapr.Actors/Builder/BuildResult.cs index c3e32babe..e9f1b7326 100644 --- a/src/Dapr.Actors/Builder/BuildResult.cs +++ b/src/Dapr.Actors/Builder/BuildResult.cs @@ -11,15 +11,14 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +internal class BuildResult { - internal class BuildResult + protected BuildResult(CodeBuilderContext buildContext) { - protected BuildResult(CodeBuilderContext buildContext) - { - this.BuildContext = buildContext; - } - - public CodeBuilderContext BuildContext { get; } + this.BuildContext = buildContext; } -} + + public CodeBuilderContext BuildContext { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/CodeBuilderAttribute.cs b/src/Dapr.Actors/Builder/CodeBuilderAttribute.cs index 22b183922..e1887c4c0 100644 --- a/src/Dapr.Actors/Builder/CodeBuilderAttribute.cs +++ b/src/Dapr.Actors/Builder/CodeBuilderAttribute.cs @@ -11,54 +11,53 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Reflection; + +/// +/// The Attribute class to configure dyanamic code generation process for service remoting. +/// +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Interface)] +public class CodeBuilderAttribute : Attribute { - using System; - using System.Reflection; + /// + /// Initializes a new instance of the class. + /// + public CodeBuilderAttribute() + { + } /// - /// The Attribute class to configure dyanamic code generation process for service remoting. + /// Gets or sets a value indicating whether to enable debugging flag for the attribute to be used by auto code generation. /// - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Interface)] - public class CodeBuilderAttribute : Attribute + /// to get or set enable debugging flag for the attribute to be used by auto code generation. + public bool EnableDebugging { get; set; } + + internal static bool IsDebuggingEnabled(Type type = null) { - /// - /// Initializes a new instance of the class. - /// - public CodeBuilderAttribute() + var enableDebugging = false; + var entryAssembly = Assembly.GetEntryAssembly(); + + if (entryAssembly != null) { + var attribute = entryAssembly.GetCustomAttribute(); + enableDebugging = ((attribute != null) && (attribute.EnableDebugging)); } - /// - /// Gets or sets a value indicating whether to enable debugging flag for the attribute to be used by auto code generation. - /// - /// to get or set enable debugging flag for the attribute to be used by auto code generation. - public bool EnableDebugging { get; set; } - - internal static bool IsDebuggingEnabled(Type type = null) + if (!enableDebugging && (type != null)) { - var enableDebugging = false; - var entryAssembly = Assembly.GetEntryAssembly(); - - if (entryAssembly != null) - { - var attribute = entryAssembly.GetCustomAttribute(); - enableDebugging = ((attribute != null) && (attribute.EnableDebugging)); - } + var attribute = type.GetTypeInfo().Assembly.GetCustomAttribute(); + enableDebugging = ((attribute != null) && (attribute.EnableDebugging)); - if (!enableDebugging && (type != null)) + if (!enableDebugging) { - var attribute = type.GetTypeInfo().Assembly.GetCustomAttribute(); + attribute = type.GetTypeInfo().GetCustomAttribute(true); enableDebugging = ((attribute != null) && (attribute.EnableDebugging)); - - if (!enableDebugging) - { - attribute = type.GetTypeInfo().GetCustomAttribute(true); - enableDebugging = ((attribute != null) && (attribute.EnableDebugging)); - } } - - return enableDebugging; } + + return enableDebugging; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/CodeBuilderContext.cs b/src/Dapr.Actors/Builder/CodeBuilderContext.cs index e8e0ec5f6..0cba22760 100644 --- a/src/Dapr.Actors/Builder/CodeBuilderContext.cs +++ b/src/Dapr.Actors/Builder/CodeBuilderContext.cs @@ -11,37 +11,36 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System.Reflection.Emit; + +internal class CodeBuilderContext { - using System.Reflection.Emit; + private readonly bool enableDebugging; - internal class CodeBuilderContext + public CodeBuilderContext(string assemblyName, string assemblyNamespace, bool enableDebugging = false) { - private readonly bool enableDebugging; - - public CodeBuilderContext(string assemblyName, string assemblyNamespace, bool enableDebugging = false) - { - this.AssemblyNamespace = assemblyNamespace; - this.enableDebugging = enableDebugging; + this.AssemblyNamespace = assemblyNamespace; + this.enableDebugging = enableDebugging; - this.AssemblyBuilder = CodeBuilderUtils.CreateAssemblyBuilder(assemblyName); - this.ModuleBuilder = CodeBuilderUtils.CreateModuleBuilder(this.AssemblyBuilder, assemblyName); - } + this.AssemblyBuilder = CodeBuilderUtils.CreateAssemblyBuilder(assemblyName); + this.ModuleBuilder = CodeBuilderUtils.CreateModuleBuilder(this.AssemblyBuilder, assemblyName); + } - public AssemblyBuilder AssemblyBuilder { get; } + public AssemblyBuilder AssemblyBuilder { get; } - public ModuleBuilder ModuleBuilder { get; } + public ModuleBuilder ModuleBuilder { get; } - public string AssemblyNamespace { get; } + public string AssemblyNamespace { get; } - public void Complete() + public void Complete() + { + if (this.enableDebugging) { - if (this.enableDebugging) - { #if !DotNetCoreClr this.assemblyBuilder.Save(this.assemblyBuilder.GetName().Name + ".dll"); #endif - } } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/CodeBuilderModule.cs b/src/Dapr.Actors/Builder/CodeBuilderModule.cs index e2d1738f1..021e9a7b4 100644 --- a/src/Dapr.Actors/Builder/CodeBuilderModule.cs +++ b/src/Dapr.Actors/Builder/CodeBuilderModule.cs @@ -11,29 +11,28 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; - using Dapr.Actors.Description; +namespace Dapr.Actors.Builder; + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Dapr.Actors.Description; - internal abstract class CodeBuilderModule +internal abstract class CodeBuilderModule +{ + protected CodeBuilderModule(ICodeBuilder codeBuilder) { - protected CodeBuilderModule(ICodeBuilder codeBuilder) - { - this.CodeBuilder = codeBuilder; - } + this.CodeBuilder = codeBuilder; + } - protected ICodeBuilder CodeBuilder { get; } + protected ICodeBuilder CodeBuilder { get; } - protected static IReadOnlyDictionary GetMethodNameMap(InterfaceDescription interfaceDescription) - { - var methodNameMap = interfaceDescription.Methods.ToDictionary( - methodDescription => methodDescription.Id, - methodDescription => methodDescription.Name); + protected static IReadOnlyDictionary GetMethodNameMap(InterfaceDescription interfaceDescription) + { + var methodNameMap = interfaceDescription.Methods.ToDictionary( + methodDescription => methodDescription.Id, + methodDescription => methodDescription.Name); - return new ReadOnlyDictionary(methodNameMap); - } + return new ReadOnlyDictionary(methodNameMap); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/CodeBuilderUtils.cs b/src/Dapr.Actors/Builder/CodeBuilderUtils.cs index f49048800..6640dfed9 100644 --- a/src/Dapr.Actors/Builder/CodeBuilderUtils.cs +++ b/src/Dapr.Actors/Builder/CodeBuilderUtils.cs @@ -11,234 +11,233 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; + +internal static class CodeBuilderUtils { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Reflection.Emit; - using System.Runtime.Serialization; - - internal static class CodeBuilderUtils - { - private static readonly ConstructorInfo DcAttrCtorInfo; - private static readonly PropertyInfo DcAttrNamePropInfo; - private static readonly PropertyInfo DcAttrNamespacePropInfo; - private static readonly ConstructorInfo DmAttrCtorInfo; - private static readonly PropertyInfo DmAttrIsRequiredPropInfo; - private static readonly object[] EmptyObjectArray = { }; - - static CodeBuilderUtils() - { - var dcAttrType = typeof(DataContractAttribute); - DcAttrCtorInfo = dcAttrType.GetConstructor(Type.EmptyTypes); - DcAttrNamePropInfo = dcAttrType.GetProperty("Name"); - DcAttrNamespacePropInfo = dcAttrType.GetProperty("Namespace"); - - var dmAttrType = typeof(DataMemberAttribute); - DmAttrCtorInfo = dmAttrType.GetConstructor(Type.EmptyTypes); - DmAttrIsRequiredPropInfo = dmAttrType.GetProperty("IsRequired"); - } + private static readonly ConstructorInfo DcAttrCtorInfo; + private static readonly PropertyInfo DcAttrNamePropInfo; + private static readonly PropertyInfo DcAttrNamespacePropInfo; + private static readonly ConstructorInfo DmAttrCtorInfo; + private static readonly PropertyInfo DmAttrIsRequiredPropInfo; + private static readonly object[] EmptyObjectArray = { }; + + static CodeBuilderUtils() + { + var dcAttrType = typeof(DataContractAttribute); + DcAttrCtorInfo = dcAttrType.GetConstructor(Type.EmptyTypes); + DcAttrNamePropInfo = dcAttrType.GetProperty("Name"); + DcAttrNamespacePropInfo = dcAttrType.GetProperty("Namespace"); + + var dmAttrType = typeof(DataMemberAttribute); + DmAttrCtorInfo = dmAttrType.GetConstructor(Type.EmptyTypes); + DmAttrIsRequiredPropInfo = dmAttrType.GetProperty("IsRequired"); + } - public static AssemblyBuilder CreateAssemblyBuilder(string assemblyName) - { - return AssemblyBuilder.DefineDynamicAssembly( - new AssemblyName(assemblyName), - AssemblyBuilderAccess.RunAndCollect); - } + public static AssemblyBuilder CreateAssemblyBuilder(string assemblyName) + { + return AssemblyBuilder.DefineDynamicAssembly( + new AssemblyName(assemblyName), + AssemblyBuilderAccess.RunAndCollect); + } - public static ModuleBuilder CreateModuleBuilder( - AssemblyBuilder assemblyBuilder, - string moduleName) + public static ModuleBuilder CreateModuleBuilder( + AssemblyBuilder assemblyBuilder, + string moduleName) + { + return assemblyBuilder.DefineDynamicModule(moduleName); + } + + public static TypeBuilder CreateClassBuilder( + ModuleBuilder moduleBuilder, + string ns, + string className, + Type baseType = null) + { + if (!string.IsNullOrEmpty(ns)) { - return assemblyBuilder.DefineDynamicModule(moduleName); + className = string.Concat(ns, ".", className); } - public static TypeBuilder CreateClassBuilder( - ModuleBuilder moduleBuilder, - string ns, - string className, - Type baseType = null) + if (baseType != null) { - if (!string.IsNullOrEmpty(ns)) - { - className = string.Concat(ns, ".", className); - } - - if (baseType != null) - { - return moduleBuilder.DefineType( - className, - TypeAttributes.Public | TypeAttributes.Class, - baseType); - } - return moduleBuilder.DefineType( className, - TypeAttributes.Public | TypeAttributes.Class); + TypeAttributes.Public | TypeAttributes.Class, + baseType); } - public static TypeBuilder CreateClassBuilder( - ModuleBuilder moduleBuilder, - string ns, - string className, - IEnumerable interfaces = null) - { - return CreateClassBuilder(moduleBuilder, ns, className, null, interfaces); - } + return moduleBuilder.DefineType( + className, + TypeAttributes.Public | TypeAttributes.Class); + } + + public static TypeBuilder CreateClassBuilder( + ModuleBuilder moduleBuilder, + string ns, + string className, + IEnumerable interfaces = null) + { + return CreateClassBuilder(moduleBuilder, ns, className, null, interfaces); + } - public static TypeBuilder CreateClassBuilder( - ModuleBuilder moduleBuilder, - string ns, - string className, - Type baseType, - IEnumerable interfaces) + public static TypeBuilder CreateClassBuilder( + ModuleBuilder moduleBuilder, + string ns, + string className, + Type baseType, + IEnumerable interfaces) + { + var typeBuilder = CreateClassBuilder(moduleBuilder, ns, className, baseType); + if (interfaces != null) { - var typeBuilder = CreateClassBuilder(moduleBuilder, ns, className, baseType); - if (interfaces != null) + foreach (var interfaceType in interfaces) { - foreach (var interfaceType in interfaces) - { - typeBuilder.AddInterfaceImplementation(interfaceType); - } + typeBuilder.AddInterfaceImplementation(interfaceType); } - - return typeBuilder; } - public static FieldBuilder CreateFieldBuilder(TypeBuilder typeBuilder, Type fieldType, string fieldName) - { - return typeBuilder.DefineField( - fieldName, - fieldType, - FieldAttributes.Public); - } + return typeBuilder; + } - public static MethodBuilder CreatePublicMethodBuilder( - TypeBuilder typeBuilder, - string methodName) - { - return typeBuilder.DefineMethod( - methodName, - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual); - } + public static FieldBuilder CreateFieldBuilder(TypeBuilder typeBuilder, Type fieldType, string fieldName) + { + return typeBuilder.DefineField( + fieldName, + fieldType, + FieldAttributes.Public); + } - public static MethodBuilder CreatePublicMethodBuilder( - TypeBuilder typeBuilder, - string methodName, - Type returnType, - params Type[] parameterTypes) - { - return typeBuilder.DefineMethod( - methodName, - (MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual), - returnType, - parameterTypes); - } + public static MethodBuilder CreatePublicMethodBuilder( + TypeBuilder typeBuilder, + string methodName) + { + return typeBuilder.DefineMethod( + methodName, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual); + } - public static MethodBuilder CreateProtectedMethodBuilder( - TypeBuilder typeBuilder, - string methodName, - Type returnType, - params Type[] parameterTypes) - { - return typeBuilder.DefineMethod( - methodName, - (MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual), - returnType, - parameterTypes); - } + public static MethodBuilder CreatePublicMethodBuilder( + TypeBuilder typeBuilder, + string methodName, + Type returnType, + params Type[] parameterTypes) + { + return typeBuilder.DefineMethod( + methodName, + (MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual), + returnType, + parameterTypes); + } - public static MethodBuilder CreateExplitInterfaceMethodBuilder( - TypeBuilder typeBuilder, - MethodInfo interfaceMethod) - { - var parameters = interfaceMethod.GetParameters(); - var parameterTypes = parameters.Select(pi => pi.ParameterType).ToArray(); - - var methodAttrs = (MethodAttributes.Private | - MethodAttributes.HideBySig | - MethodAttributes.NewSlot | - MethodAttributes.Virtual | - MethodAttributes.Final); - - var methodBuilder = typeBuilder.DefineMethod( - string.Concat(interfaceMethod.DeclaringType.Name, ".", interfaceMethod.Name), - methodAttrs, - interfaceMethod.ReturnType, - parameterTypes); - - typeBuilder.DefineMethodOverride(methodBuilder, interfaceMethod); - return methodBuilder; - } + public static MethodBuilder CreateProtectedMethodBuilder( + TypeBuilder typeBuilder, + string methodName, + Type returnType, + params Type[] parameterTypes) + { + return typeBuilder.DefineMethod( + methodName, + (MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual), + returnType, + parameterTypes); + } - #region Data Contract Type Generation Utilities + public static MethodBuilder CreateExplitInterfaceMethodBuilder( + TypeBuilder typeBuilder, + MethodInfo interfaceMethod) + { + var parameters = interfaceMethod.GetParameters(); + var parameterTypes = parameters.Select(pi => pi.ParameterType).ToArray(); + + var methodAttrs = (MethodAttributes.Private | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual | + MethodAttributes.Final); + + var methodBuilder = typeBuilder.DefineMethod( + string.Concat(interfaceMethod.DeclaringType.Name, ".", interfaceMethod.Name), + methodAttrs, + interfaceMethod.ReturnType, + parameterTypes); + + typeBuilder.DefineMethodOverride(methodBuilder, interfaceMethod); + return methodBuilder; + } - public static TypeBuilder CreateDataContractTypeBuilder( - ModuleBuilder moduleBuilder, - string ns, - string typeName, - string dcNamespace = null, - string dcName = null) - { - var typeBuilder = CreateClassBuilder(moduleBuilder, ns, typeName, Type.EmptyTypes); - AddDataContractAttribute(typeBuilder, dcNamespace, dcName); - return typeBuilder; - } + #region Data Contract Type Generation Utilities - public static void AddDataMemberField(TypeBuilder dcTypeBuilder, Type fieldType, string fieldName) - { - var fieldBuilder = CreateFieldBuilder(dcTypeBuilder, fieldType, fieldName); - fieldBuilder.SetCustomAttribute(CreateCustomDataMemberAttributeBuilder()); - } + public static TypeBuilder CreateDataContractTypeBuilder( + ModuleBuilder moduleBuilder, + string ns, + string typeName, + string dcNamespace = null, + string dcName = null) + { + var typeBuilder = CreateClassBuilder(moduleBuilder, ns, typeName, Type.EmptyTypes); + AddDataContractAttribute(typeBuilder, dcNamespace, dcName); + return typeBuilder; + } + + public static void AddDataMemberField(TypeBuilder dcTypeBuilder, Type fieldType, string fieldName) + { + var fieldBuilder = CreateFieldBuilder(dcTypeBuilder, fieldType, fieldName); + fieldBuilder.SetCustomAttribute(CreateCustomDataMemberAttributeBuilder()); + } - private static void AddDataContractAttribute(TypeBuilder typeBuilder, string dcNamespace, string dcName) + private static void AddDataContractAttribute(TypeBuilder typeBuilder, string dcNamespace, string dcName) + { + typeBuilder.SetCustomAttribute(CreateCustomDataContractAttributeBuilder(dcNamespace, dcName)); + } + + private static CustomAttributeBuilder CreateCustomDataContractAttributeBuilder(string dcNamespace, string dcName) + { + if (string.IsNullOrEmpty(dcName) && string.IsNullOrEmpty(dcNamespace)) { - typeBuilder.SetCustomAttribute(CreateCustomDataContractAttributeBuilder(dcNamespace, dcName)); + return new CustomAttributeBuilder(DcAttrCtorInfo, EmptyObjectArray); } - private static CustomAttributeBuilder CreateCustomDataContractAttributeBuilder(string dcNamespace, string dcName) + if (string.IsNullOrEmpty(dcName)) { - if (string.IsNullOrEmpty(dcName) && string.IsNullOrEmpty(dcNamespace)) - { - return new CustomAttributeBuilder(DcAttrCtorInfo, EmptyObjectArray); - } - - if (string.IsNullOrEmpty(dcName)) - { - return new CustomAttributeBuilder( - DcAttrCtorInfo, - EmptyObjectArray, - new[] { DcAttrNamespacePropInfo }, - new object[] { dcNamespace }); - } - - if (string.IsNullOrEmpty(dcNamespace)) - { - return new CustomAttributeBuilder( - DcAttrCtorInfo, - EmptyObjectArray, - new[] { DcAttrNamePropInfo }, - new object[] { dcName }); - } - return new CustomAttributeBuilder( DcAttrCtorInfo, EmptyObjectArray, - new[] { DcAttrNamespacePropInfo, DcAttrNamePropInfo }, - new object[] { dcNamespace, dcName }); + new[] { DcAttrNamespacePropInfo }, + new object[] { dcNamespace }); } - private static CustomAttributeBuilder CreateCustomDataMemberAttributeBuilder() + if (string.IsNullOrEmpty(dcNamespace)) { return new CustomAttributeBuilder( - DmAttrCtorInfo, + DcAttrCtorInfo, EmptyObjectArray, - new[] { DmAttrIsRequiredPropInfo }, - new object[] { false }); + new[] { DcAttrNamePropInfo }, + new object[] { dcName }); } - #endregion + return new CustomAttributeBuilder( + DcAttrCtorInfo, + EmptyObjectArray, + new[] { DcAttrNamespacePropInfo, DcAttrNamePropInfo }, + new object[] { dcNamespace, dcName }); } -} + + private static CustomAttributeBuilder CreateCustomDataMemberAttributeBuilder() + { + return new CustomAttributeBuilder( + DmAttrCtorInfo, + EmptyObjectArray, + new[] { DmAttrIsRequiredPropInfo }, + new object[] { false }); + } + + #endregion +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ICodeBuilder.cs b/src/Dapr.Actors/Builder/ICodeBuilder.cs index 4013f74a3..dbd47b7b0 100644 --- a/src/Dapr.Actors/Builder/ICodeBuilder.cs +++ b/src/Dapr.Actors/Builder/ICodeBuilder.cs @@ -11,39 +11,38 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using System; +namespace Dapr.Actors.Builder; + +using System; +/// +/// Represents an interface for generating the code to support communication. +/// +internal interface ICodeBuilder +{ /// - /// Represents an interface for generating the code to support communication. + /// Gets the interface for getting the names of the generated code (types, interfaces, methods etc.) /// - internal interface ICodeBuilder - { - /// - /// Gets the interface for getting the names of the generated code (types, interfaces, methods etc.) - /// - ICodeBuilderNames Names { get; } + ICodeBuilderNames Names { get; } - /// - /// Gets or builds a type that can send the communication messages to the object implementing the specified interface. - /// - /// Interface for which to generate the method dispatcher. - /// A containing the dispatcher to dispatch the messages destined the specified interfaces. - MethodDispatcherBuildResult GetOrBuilderMethodDispatcher(Type interfaceType); + /// + /// Gets or builds a type that can send the communication messages to the object implementing the specified interface. + /// + /// Interface for which to generate the method dispatcher. + /// A containing the dispatcher to dispatch the messages destined the specified interfaces. + MethodDispatcherBuildResult GetOrBuilderMethodDispatcher(Type interfaceType); - /// - /// Gets or builds a communication message body types that can store the method arguments of the specified interface. - /// - /// Interface for which to generate the method body types. - /// A containing the method body types for each of the methods of the specified interface. - MethodBodyTypesBuildResult GetOrBuildMethodBodyTypes(Type interfaceType); + /// + /// Gets or builds a communication message body types that can store the method arguments of the specified interface. + /// + /// Interface for which to generate the method body types. + /// A containing the method body types for each of the methods of the specified interface. + MethodBodyTypesBuildResult GetOrBuildMethodBodyTypes(Type interfaceType); - /// - /// Gets or builds a factory object that can generate communication proxy for the specified interface. - /// - /// Interface for which to generate the proxy factory object. - /// A containing the generator for communication proxy for the speficifed interface. - ActorProxyGeneratorBuildResult GetOrBuildProxyGenerator(Type interfaceType); - } -} + /// + /// Gets or builds a factory object that can generate communication proxy for the specified interface. + /// + /// Interface for which to generate the proxy factory object. + /// A containing the generator for communication proxy for the speficifed interface. + ActorProxyGeneratorBuildResult GetOrBuildProxyGenerator(Type interfaceType); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/ICodeBuilderNames.cs b/src/Dapr.Actors/Builder/ICodeBuilderNames.cs index 9b42c8d58..420013fbc 100644 --- a/src/Dapr.Actors/Builder/ICodeBuilderNames.cs +++ b/src/Dapr.Actors/Builder/ICodeBuilderNames.cs @@ -11,116 +11,115 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; + +/// +/// Provides the names for the generated types, methods, arguments, members etc. +/// +internal interface ICodeBuilderNames { - using System; - - /// - /// Provides the names for the generated types, methods, arguments, members etc. - /// - internal interface ICodeBuilderNames - { - /// - /// Gets the name for the interface Id field. - /// - string InterfaceId { get; } - - /// - /// Gets the name for the method Id field. - /// - string MethodId { get; } - - /// - /// Gets the name for the retval field. - /// - string RetVal { get; } - - /// - /// Gets the name for the request body field. - /// - string RequestBody { get; } - - /// - /// Gets the name of the assembly in which to generate the method body types. - /// - /// The name of the remoted interface. - /// The assembly name for the method body types. - string GetMethodBodyTypesAssemblyName(Type interfaceType); - - /// - /// Gets the namespace of the assembly in which to generate the method body types. - /// - /// The name of the remoted interface. - /// The assembly namespace for the method body types. - string GetMethodBodyTypesAssemblyNamespace(Type interfaceType); - - /// - /// Gets the name of the request body type for the specified method. - /// - /// Name of the method whose parameters needs to be wraped in the body type. - /// The name of the request body type. - string GetRequestBodyTypeName(string methodName); - - /// - /// Gets the name of the response body type for the specified method. - /// - /// Name of the method whose return value needs to be wraped in the body type. - /// The name of the response body type. - string GetResponseBodyTypeName(string methodName); - - /// - /// Gets the data contract namespace for the generated types. - /// - /// The data contract namespace. - string GetDataContractNamespace(); - - /// - /// Gets the name of the assembly in which to generate the method dispatcher type. - /// - /// The remoted interface type. - /// The name of the assembly for method disptacher. - string GetMethodDispatcherAssemblyName(Type interfaceType); - - /// - /// Gets the namespace of the assembly in which to generate the method dispatcher type. - /// - /// The remoted interface type. - /// The namespace of the assembly for method disptacher. - string GetMethodDispatcherAssemblyNamespace(Type interfaceType); - - /// - /// Gets the name of the method dispatcher class for dispatching methods to the implementation of the specified interface. - /// - /// The remoted interface type. - /// The name of the method dispatcher class. - string GetMethodDispatcherClassName(Type interfaceType); - - /// - /// Gets the name of the assembly in which to generate the proxy of the specified remoted interface. - /// - /// The remoted interface type. - /// The name of the assembly for proxy. - string GetProxyAssemblyName(Type interfaceType); - - /// - /// Gets the namespace of the assembly in which to generate the proxy of the specified remoted interface. - /// - /// The remoted interface type. - /// The namespace of the assembly for proxy. - string GetProxyAssemblyNamespace(Type interfaceType); - - /// - /// Gets the name of the proxy class for the specified interface. - /// - /// The remoted interface type. - /// The name of proxy class. - string GetProxyClassName(Type interfaceType); - - /// - /// Gets the name of the proxy factory (or activator) class for the specified interface. - /// - /// The remoted interface type. - /// The name of proxy activator class. - string GetProxyActivatorClassName(Type interfaceType); - } -} + /// + /// Gets the name for the interface Id field. + /// + string InterfaceId { get; } + + /// + /// Gets the name for the method Id field. + /// + string MethodId { get; } + + /// + /// Gets the name for the retval field. + /// + string RetVal { get; } + + /// + /// Gets the name for the request body field. + /// + string RequestBody { get; } + + /// + /// Gets the name of the assembly in which to generate the method body types. + /// + /// The name of the remoted interface. + /// The assembly name for the method body types. + string GetMethodBodyTypesAssemblyName(Type interfaceType); + + /// + /// Gets the namespace of the assembly in which to generate the method body types. + /// + /// The name of the remoted interface. + /// The assembly namespace for the method body types. + string GetMethodBodyTypesAssemblyNamespace(Type interfaceType); + + /// + /// Gets the name of the request body type for the specified method. + /// + /// Name of the method whose parameters needs to be wraped in the body type. + /// The name of the request body type. + string GetRequestBodyTypeName(string methodName); + + /// + /// Gets the name of the response body type for the specified method. + /// + /// Name of the method whose return value needs to be wraped in the body type. + /// The name of the response body type. + string GetResponseBodyTypeName(string methodName); + + /// + /// Gets the data contract namespace for the generated types. + /// + /// The data contract namespace. + string GetDataContractNamespace(); + + /// + /// Gets the name of the assembly in which to generate the method dispatcher type. + /// + /// The remoted interface type. + /// The name of the assembly for method disptacher. + string GetMethodDispatcherAssemblyName(Type interfaceType); + + /// + /// Gets the namespace of the assembly in which to generate the method dispatcher type. + /// + /// The remoted interface type. + /// The namespace of the assembly for method disptacher. + string GetMethodDispatcherAssemblyNamespace(Type interfaceType); + + /// + /// Gets the name of the method dispatcher class for dispatching methods to the implementation of the specified interface. + /// + /// The remoted interface type. + /// The name of the method dispatcher class. + string GetMethodDispatcherClassName(Type interfaceType); + + /// + /// Gets the name of the assembly in which to generate the proxy of the specified remoted interface. + /// + /// The remoted interface type. + /// The name of the assembly for proxy. + string GetProxyAssemblyName(Type interfaceType); + + /// + /// Gets the namespace of the assembly in which to generate the proxy of the specified remoted interface. + /// + /// The remoted interface type. + /// The namespace of the assembly for proxy. + string GetProxyAssemblyNamespace(Type interfaceType); + + /// + /// Gets the name of the proxy class for the specified interface. + /// + /// The remoted interface type. + /// The name of proxy class. + string GetProxyClassName(Type interfaceType); + + /// + /// Gets the name of the proxy factory (or activator) class for the specified interface. + /// + /// The remoted interface type. + /// The name of proxy activator class. + string GetProxyActivatorClassName(Type interfaceType); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/IProxyActivator.cs b/src/Dapr.Actors/Builder/IProxyActivator.cs index d895961f5..b62c04ba3 100644 --- a/src/Dapr.Actors/Builder/IProxyActivator.cs +++ b/src/Dapr.Actors/Builder/IProxyActivator.cs @@ -11,19 +11,18 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using Dapr.Actors.Client; +namespace Dapr.Actors.Builder; + +using Dapr.Actors.Client; +/// +/// Interface to create objects. +/// +public interface IProxyActivator +{ /// - /// Interface to create objects. + /// Create the instance of the generated proxy type. /// - public interface IProxyActivator - { - /// - /// Create the instance of the generated proxy type. - /// - /// An instance of the generated proxy as type. - IActorProxy CreateInstance(); - } -} + /// An instance of the generated proxy as type. + IActorProxy CreateInstance(); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/InterfaceDetails.cs b/src/Dapr.Actors/Builder/InterfaceDetails.cs index eaec6b890..79dbd9657 100644 --- a/src/Dapr.Actors/Builder/InterfaceDetails.cs +++ b/src/Dapr.Actors/Builder/InterfaceDetails.cs @@ -11,36 +11,35 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using System; - using System.Collections.Generic; +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Generic; - internal class InterfaceDetails +internal class InterfaceDetails +{ + internal InterfaceDetails() { - internal InterfaceDetails() - { - this.Id = 0; - this.MethodNames = new Dictionary(); - this.RequestWrappedKnownTypes = new List(); - this.ResponseWrappedKnownTypes = new List(); - this.ResponseKnownTypes = new List(); - this.ServiceInterfaceType = null; - this.RequestKnownTypes = new List(); - } + this.Id = 0; + this.MethodNames = new Dictionary(); + this.RequestWrappedKnownTypes = new List(); + this.ResponseWrappedKnownTypes = new List(); + this.ResponseKnownTypes = new List(); + this.ServiceInterfaceType = null; + this.RequestKnownTypes = new List(); + } - public Type ServiceInterfaceType { get; internal set; } + public Type ServiceInterfaceType { get; internal set; } - public int Id { get; internal set; } + public int Id { get; internal set; } - public List RequestKnownTypes { get; internal set; } + public List RequestKnownTypes { get; internal set; } - public List ResponseKnownTypes { get; internal set; } + public List ResponseKnownTypes { get; internal set; } - public IEnumerable RequestWrappedKnownTypes { get; internal set; } + public IEnumerable RequestWrappedKnownTypes { get; internal set; } - public IEnumerable ResponseWrappedKnownTypes { get; internal set; } + public IEnumerable ResponseWrappedKnownTypes { get; internal set; } - public Dictionary MethodNames { get; internal set; } - } -} + public Dictionary MethodNames { get; internal set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/InterfaceDetailsStore.cs b/src/Dapr.Actors/Builder/InterfaceDetailsStore.cs index 08ab7ffd4..c87602c64 100644 --- a/src/Dapr.Actors/Builder/InterfaceDetailsStore.cs +++ b/src/Dapr.Actors/Builder/InterfaceDetailsStore.cs @@ -11,89 +11,88 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Dapr.Actors.Description; + +internal class InterfaceDetailsStore { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using Dapr.Actors.Description; + private readonly ConcurrentDictionary knownTypesMap = + new ConcurrentDictionary(); - internal class InterfaceDetailsStore - { - private readonly ConcurrentDictionary knownTypesMap = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary interfaceIdMapping = + new ConcurrentDictionary(); - private readonly ConcurrentDictionary interfaceIdMapping = - new ConcurrentDictionary(); + public bool TryGetKnownTypes(int interfaceId, out InterfaceDetails interfaceDetails) + { + return this.knownTypesMap.TryGetValue(interfaceId, out interfaceDetails); + } - public bool TryGetKnownTypes(int interfaceId, out InterfaceDetails interfaceDetails) + public bool TryGetKnownTypes(string interfaceName, out InterfaceDetails interfaceDetails) + { + if (!this.interfaceIdMapping.TryGetValue(interfaceName, out var interfaceId)) { - return this.knownTypesMap.TryGetValue(interfaceId, out interfaceDetails); + // TODO : Add EventSource diagnostics + interfaceDetails = null; + return false; } - public bool TryGetKnownTypes(string interfaceName, out InterfaceDetails interfaceDetails) - { - if (!this.interfaceIdMapping.TryGetValue(interfaceName, out var interfaceId)) - { - // TODO : Add EventSource diagnostics - interfaceDetails = null; - return false; - } - - return this.knownTypesMap.TryGetValue(interfaceId, out interfaceDetails); - } + return this.knownTypesMap.TryGetValue(interfaceId, out interfaceDetails); + } - public void UpdateKnownTypeDetail(InterfaceDescription interfaceDescription, MethodBodyTypesBuildResult methodBodyTypesBuildResult) + public void UpdateKnownTypeDetail(InterfaceDescription interfaceDescription, MethodBodyTypesBuildResult methodBodyTypesBuildResult) + { + var responseKnownTypes = new List(); + var requestKnownType = new List(); + foreach (var entry in interfaceDescription.Methods) { - var responseKnownTypes = new List(); - var requestKnownType = new List(); - foreach (var entry in interfaceDescription.Methods) + if (TypeUtility.IsTaskType(entry.ReturnType) && entry.ReturnType.GetTypeInfo().IsGenericType) { - if (TypeUtility.IsTaskType(entry.ReturnType) && entry.ReturnType.GetTypeInfo().IsGenericType) + var returnType = entry.MethodInfo.ReturnType.GetGenericArguments()[0]; + if (!responseKnownTypes.Contains(returnType)) { - var returnType = entry.MethodInfo.ReturnType.GetGenericArguments()[0]; - if (!responseKnownTypes.Contains(returnType)) - { - responseKnownTypes.Add(returnType); - } + responseKnownTypes.Add(returnType); } - - requestKnownType.AddRange(entry.MethodInfo.GetParameters() - .ToList() - .Select(p => p.ParameterType) - .Except(requestKnownType)); } - var knownType = new InterfaceDetails - { - Id = interfaceDescription.Id, - ServiceInterfaceType = interfaceDescription.InterfaceType, - RequestKnownTypes = requestKnownType, - ResponseKnownTypes = responseKnownTypes, - MethodNames = interfaceDescription.Methods.ToDictionary(item => item.Name, item => item.Id), - RequestWrappedKnownTypes = methodBodyTypesBuildResult.GetRequestBodyTypes(), - ResponseWrappedKnownTypes = methodBodyTypesBuildResult.GetResponseBodyTypes(), - }; - this.UpdateKnownTypes(interfaceDescription.Id, interfaceDescription.InterfaceType.FullName, knownType); + requestKnownType.AddRange(entry.MethodInfo.GetParameters() + .ToList() + .Select(p => p.ParameterType) + .Except(requestKnownType)); } - private void UpdateKnownTypes( - int interfaceId, - string interfaceName, - InterfaceDetails knownTypes) + var knownType = new InterfaceDetails { - if (this.knownTypesMap.ContainsKey(interfaceId)) - { - // TODO : Add EventSource diagnostics - return; - } + Id = interfaceDescription.Id, + ServiceInterfaceType = interfaceDescription.InterfaceType, + RequestKnownTypes = requestKnownType, + ResponseKnownTypes = responseKnownTypes, + MethodNames = interfaceDescription.Methods.ToDictionary(item => item.Name, item => item.Id), + RequestWrappedKnownTypes = methodBodyTypesBuildResult.GetRequestBodyTypes(), + ResponseWrappedKnownTypes = methodBodyTypesBuildResult.GetResponseBodyTypes(), + }; + this.UpdateKnownTypes(interfaceDescription.Id, interfaceDescription.InterfaceType.FullName, knownType); + } - if (this.knownTypesMap.TryAdd(interfaceId, knownTypes)) - { - this.interfaceIdMapping.TryAdd(interfaceName, interfaceId); - } + private void UpdateKnownTypes( + int interfaceId, + string interfaceName, + InterfaceDetails knownTypes) + { + if (this.knownTypesMap.ContainsKey(interfaceId)) + { + // TODO : Add EventSource diagnostics + return; + } + + if (this.knownTypesMap.TryAdd(interfaceId, knownTypes)) + { + this.interfaceIdMapping.TryAdd(interfaceName, interfaceId); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/MethodBodyTypes.cs b/src/Dapr.Actors/Builder/MethodBodyTypes.cs index 0ee0aa899..7bed0db54 100644 --- a/src/Dapr.Actors/Builder/MethodBodyTypes.cs +++ b/src/Dapr.Actors/Builder/MethodBodyTypes.cs @@ -11,16 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using System; +namespace Dapr.Actors.Builder; + +using System; - internal class MethodBodyTypes - { - public Type RequestBodyType { get; set; } +internal class MethodBodyTypes +{ + public Type RequestBodyType { get; set; } - public Type ResponseBodyType { get; set; } + public Type ResponseBodyType { get; set; } - public bool HasCancellationTokenArgument { get; set; } - } -} + public bool HasCancellationTokenArgument { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/MethodBodyTypesBuildResult.cs b/src/Dapr.Actors/Builder/MethodBodyTypesBuildResult.cs index 1cfb711dc..88728f183 100644 --- a/src/Dapr.Actors/Builder/MethodBodyTypesBuildResult.cs +++ b/src/Dapr.Actors/Builder/MethodBodyTypesBuildResult.cs @@ -11,53 +11,52 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using System; - using System.Collections.Generic; +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Generic; - internal class MethodBodyTypesBuildResult : BuildResult +internal class MethodBodyTypesBuildResult : BuildResult +{ + public MethodBodyTypesBuildResult(CodeBuilderContext buildContext) + : base(buildContext) { - public MethodBodyTypesBuildResult(CodeBuilderContext buildContext) - : base(buildContext) - { - } + } - // methodName, methodBodyTypes (RequestType, ResponseType) map - public IDictionary MethodBodyTypesMap { get; set; } + // methodName, methodBodyTypes (RequestType, ResponseType) map + public IDictionary MethodBodyTypesMap { get; set; } - public IEnumerable GetRequestBodyTypes() + public IEnumerable GetRequestBodyTypes() + { + var result = new List(); + if (this.MethodBodyTypesMap != null) { - var result = new List(); - if (this.MethodBodyTypesMap != null) + foreach (var item in this.MethodBodyTypesMap) { - foreach (var item in this.MethodBodyTypesMap) + if (item.Value.RequestBodyType != null) { - if (item.Value.RequestBodyType != null) - { - result.Add(item.Value.RequestBodyType); - } + result.Add(item.Value.RequestBodyType); } } - - return result; } - public IEnumerable GetResponseBodyTypes() + return result; + } + + public IEnumerable GetResponseBodyTypes() + { + var result = new List(); + if (this.MethodBodyTypesMap != null) { - var result = new List(); - if (this.MethodBodyTypesMap != null) + foreach (var item in this.MethodBodyTypesMap) { - foreach (var item in this.MethodBodyTypesMap) + if (item.Value.ResponseBodyType != null) { - if (item.Value.ResponseBodyType != null) - { - result.Add(item.Value.ResponseBodyType); - } + result.Add(item.Value.ResponseBodyType); } } - - return result; } + + return result; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/MethodBodyTypesBuilder.cs b/src/Dapr.Actors/Builder/MethodBodyTypesBuilder.cs index f564296d2..24f812486 100644 --- a/src/Dapr.Actors/Builder/MethodBodyTypesBuilder.cs +++ b/src/Dapr.Actors/Builder/MethodBodyTypesBuilder.cs @@ -11,107 +11,106 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Collections.Generic; +using System.Reflection; +using Dapr.Actors.Description; + +internal class MethodBodyTypesBuilder : CodeBuilderModule { - using System; - using System.Collections.Generic; - using System.Reflection; - using Dapr.Actors.Description; + public MethodBodyTypesBuilder(ICodeBuilder codeBuilder) + : base(codeBuilder) + { + } - internal class MethodBodyTypesBuilder : CodeBuilderModule + public MethodBodyTypesBuildResult Build(InterfaceDescription interfaceDescription) { - public MethodBodyTypesBuilder(ICodeBuilder codeBuilder) - : base(codeBuilder) + var context = new CodeBuilderContext( + assemblyName: this.CodeBuilder.Names.GetMethodBodyTypesAssemblyName(interfaceDescription.InterfaceType), + assemblyNamespace: this.CodeBuilder.Names.GetMethodBodyTypesAssemblyNamespace(interfaceDescription.InterfaceType), + enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(interfaceDescription.InterfaceType)); + + var result = new MethodBodyTypesBuildResult(context) + { + MethodBodyTypesMap = new Dictionary(), + }; + foreach (var method in interfaceDescription.Methods) { + result.MethodBodyTypesMap.Add( + method.Name, + Build(this.CodeBuilder.Names, context, method)); } - public MethodBodyTypesBuildResult Build(InterfaceDescription interfaceDescription) + context.Complete(); + return result; + } + + private static MethodBodyTypes Build( + ICodeBuilderNames codeBuilderNames, + CodeBuilderContext context, + MethodDescription methodDescription) + { + var methodDataTypes = new MethodBodyTypes() { - var context = new CodeBuilderContext( - assemblyName: this.CodeBuilder.Names.GetMethodBodyTypesAssemblyName(interfaceDescription.InterfaceType), - assemblyNamespace: this.CodeBuilder.Names.GetMethodBodyTypesAssemblyNamespace(interfaceDescription.InterfaceType), - enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(interfaceDescription.InterfaceType)); - - var result = new MethodBodyTypesBuildResult(context) - { - MethodBodyTypesMap = new Dictionary(), - }; - foreach (var method in interfaceDescription.Methods) - { - result.MethodBodyTypesMap.Add( - method.Name, - Build(this.CodeBuilder.Names, context, method)); - } - - context.Complete(); - return result; - } + RequestBodyType = null, + ResponseBodyType = null, + HasCancellationTokenArgument = methodDescription.HasCancellationToken, + }; - private static MethodBodyTypes Build( - ICodeBuilderNames codeBuilderNames, - CodeBuilderContext context, - MethodDescription methodDescription) + if ((methodDescription.Arguments != null) && (methodDescription.Arguments.Length != 0)) { - var methodDataTypes = new MethodBodyTypes() - { - RequestBodyType = null, - ResponseBodyType = null, - HasCancellationTokenArgument = methodDescription.HasCancellationToken, - }; - - if ((methodDescription.Arguments != null) && (methodDescription.Arguments.Length != 0)) - { - methodDataTypes.RequestBodyType = BuildRequestBodyType(codeBuilderNames, context, methodDescription); - } - - if (TypeUtility.IsTaskType(methodDescription.ReturnType) && methodDescription.ReturnType.GetTypeInfo().IsGenericType) - { - methodDataTypes.ResponseBodyType = BuildResponseBodyType(codeBuilderNames, context, methodDescription); - } - - return methodDataTypes; + methodDataTypes.RequestBodyType = BuildRequestBodyType(codeBuilderNames, context, methodDescription); } - private static Type BuildRequestBodyType( - ICodeBuilderNames codeBuilderNames, - CodeBuilderContext context, - MethodDescription methodDescription) + if (TypeUtility.IsTaskType(methodDescription.ReturnType) && methodDescription.ReturnType.GetTypeInfo().IsGenericType) { - var requestBodyTypeBuilder = CodeBuilderUtils.CreateDataContractTypeBuilder( - moduleBuilder: context.ModuleBuilder, - ns: context.AssemblyNamespace, - typeName: codeBuilderNames.GetRequestBodyTypeName(methodDescription.Name), - dcNamespace: codeBuilderNames.GetDataContractNamespace()); - - foreach (var argument in methodDescription.Arguments) - { - CodeBuilderUtils.AddDataMemberField( - dcTypeBuilder: requestBodyTypeBuilder, - fieldType: argument.ArgumentType, - fieldName: argument.Name); - } - - return requestBodyTypeBuilder.CreateTypeInfo().AsType(); + methodDataTypes.ResponseBodyType = BuildResponseBodyType(codeBuilderNames, context, methodDescription); } - private static Type BuildResponseBodyType( - ICodeBuilderNames codeBuilderNames, - CodeBuilderContext context, - MethodDescription methodDescription) - { - var responseBodyTypeBuilder = CodeBuilderUtils.CreateDataContractTypeBuilder( - moduleBuilder: context.ModuleBuilder, - ns: context.AssemblyNamespace, - typeName: codeBuilderNames.GetResponseBodyTypeName(methodDescription.Name), - dcNamespace: codeBuilderNames.GetDataContractNamespace()); + return methodDataTypes; + } - var returnDataType = methodDescription.ReturnType.GetGenericArguments()[0]; - CodeBuilderUtils.AddDataMemberField( - dcTypeBuilder: responseBodyTypeBuilder, - fieldType: returnDataType, - fieldName: codeBuilderNames.RetVal); + private static Type BuildRequestBodyType( + ICodeBuilderNames codeBuilderNames, + CodeBuilderContext context, + MethodDescription methodDescription) + { + var requestBodyTypeBuilder = CodeBuilderUtils.CreateDataContractTypeBuilder( + moduleBuilder: context.ModuleBuilder, + ns: context.AssemblyNamespace, + typeName: codeBuilderNames.GetRequestBodyTypeName(methodDescription.Name), + dcNamespace: codeBuilderNames.GetDataContractNamespace()); - return responseBodyTypeBuilder.CreateTypeInfo().AsType(); + foreach (var argument in methodDescription.Arguments) + { + CodeBuilderUtils.AddDataMemberField( + dcTypeBuilder: requestBodyTypeBuilder, + fieldType: argument.ArgumentType, + fieldName: argument.Name); } + + return requestBodyTypeBuilder.CreateTypeInfo().AsType(); + } + + private static Type BuildResponseBodyType( + ICodeBuilderNames codeBuilderNames, + CodeBuilderContext context, + MethodDescription methodDescription) + { + var responseBodyTypeBuilder = CodeBuilderUtils.CreateDataContractTypeBuilder( + moduleBuilder: context.ModuleBuilder, + ns: context.AssemblyNamespace, + typeName: codeBuilderNames.GetResponseBodyTypeName(methodDescription.Name), + dcNamespace: codeBuilderNames.GetDataContractNamespace()); + + var returnDataType = methodDescription.ReturnType.GetGenericArguments()[0]; + CodeBuilderUtils.AddDataMemberField( + dcTypeBuilder: responseBodyTypeBuilder, + fieldType: returnDataType, + fieldName: codeBuilderNames.RetVal); + + return responseBodyTypeBuilder.CreateTypeInfo().AsType(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/MethodDispatcherBuildResult.cs b/src/Dapr.Actors/Builder/MethodDispatcherBuildResult.cs index 6964dd8ea..fb29cdd64 100644 --- a/src/Dapr.Actors/Builder/MethodDispatcherBuildResult.cs +++ b/src/Dapr.Actors/Builder/MethodDispatcherBuildResult.cs @@ -11,19 +11,18 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder -{ - using System; +namespace Dapr.Actors.Builder; + +using System; - internal class MethodDispatcherBuildResult : BuildResult +internal class MethodDispatcherBuildResult : BuildResult +{ + public MethodDispatcherBuildResult(CodeBuilderContext buildContext) + : base(buildContext) { - public MethodDispatcherBuildResult(CodeBuilderContext buildContext) - : base(buildContext) - { - } + } - public Type MethodDispatcherType { get; set; } + public Type MethodDispatcherType { get; set; } - public ActorMethodDispatcherBase MethodDispatcher { get; set; } - } -} + public ActorMethodDispatcherBase MethodDispatcher { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Builder/MethodDispatcherBuilder.cs b/src/Dapr.Actors/Builder/MethodDispatcherBuilder.cs index 7bdc9276a..37ada87de 100644 --- a/src/Dapr.Actors/Builder/MethodDispatcherBuilder.cs +++ b/src/Dapr.Actors/Builder/MethodDispatcherBuilder.cs @@ -11,429 +11,428 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Builder +namespace Dapr.Actors.Builder; + +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Communication; +using Dapr.Actors.Description; + +internal class MethodDispatcherBuilder : CodeBuilderModule + where TMethodDispatcher : ActorMethodDispatcherBase { - using System; - using System.Reflection; - using System.Reflection.Emit; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Communication; - using Dapr.Actors.Description; - - internal class MethodDispatcherBuilder : CodeBuilderModule - where TMethodDispatcher : ActorMethodDispatcherBase + private static readonly MethodInfo getTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle)); + private readonly Type methodDispatcherBaseType; + private readonly MethodInfo continueWithResultMethodInfo; + private readonly MethodInfo continueWithMethodInfo; + private readonly MethodInfo checkIfitsWrapped; + + public MethodDispatcherBuilder(ICodeBuilder codeBuilder) + : base(codeBuilder) { - private static readonly MethodInfo getTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle)); - private readonly Type methodDispatcherBaseType; - private readonly MethodInfo continueWithResultMethodInfo; - private readonly MethodInfo continueWithMethodInfo; - private readonly MethodInfo checkIfitsWrapped; - - public MethodDispatcherBuilder(ICodeBuilder codeBuilder) - : base(codeBuilder) - { - this.methodDispatcherBaseType = typeof(TMethodDispatcher); - - this.continueWithResultMethodInfo = this.methodDispatcherBaseType.GetMethod( - "ContinueWithResult", - BindingFlags.Instance | BindingFlags.NonPublic); - - this.continueWithMethodInfo = this.methodDispatcherBaseType.GetMethod( - "ContinueWith", - BindingFlags.Instance | BindingFlags.NonPublic); - this.checkIfitsWrapped = this.methodDispatcherBaseType.GetMethod( - "CheckIfItsWrappedRequest", - BindingFlags.Instance | BindingFlags.NonPublic, - null, - CallingConventions.Any, - new[] { typeof(IActorRequestMessageBody) }, - null); - } + this.methodDispatcherBaseType = typeof(TMethodDispatcher); + + this.continueWithResultMethodInfo = this.methodDispatcherBaseType.GetMethod( + "ContinueWithResult", + BindingFlags.Instance | BindingFlags.NonPublic); + + this.continueWithMethodInfo = this.methodDispatcherBaseType.GetMethod( + "ContinueWith", + BindingFlags.Instance | BindingFlags.NonPublic); + this.checkIfitsWrapped = this.methodDispatcherBaseType.GetMethod( + "CheckIfItsWrappedRequest", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + CallingConventions.Any, + new[] { typeof(IActorRequestMessageBody) }, + null); + } - public MethodDispatcherBuildResult Build( - InterfaceDescription interfaceDescription) - { - var context = new CodeBuilderContext( - assemblyName: this.CodeBuilder.Names.GetMethodDispatcherAssemblyName(interfaceDescription.InterfaceType), - assemblyNamespace: this.CodeBuilder.Names.GetMethodDispatcherAssemblyNamespace(interfaceDescription - .InterfaceType), - enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(interfaceDescription.InterfaceType)); - - var result = new MethodDispatcherBuildResult(context); - - // ensure that the method body types are built - var methodBodyTypesBuildResult = - this.CodeBuilder.GetOrBuildMethodBodyTypes(interfaceDescription.InterfaceType); - - // build dispatcher class - var classBuilder = CodeBuilderUtils.CreateClassBuilder( - context.ModuleBuilder, - ns: context.AssemblyNamespace, - className: this.CodeBuilder.Names.GetMethodDispatcherClassName(interfaceDescription.InterfaceType), - baseType: this.methodDispatcherBaseType); - - this.AddCreateResponseBodyMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult); - this.AddOnDispatchAsyncMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult); - this.AddOnDispatchMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult); - - var methodNameMap = GetMethodNameMap(interfaceDescription); - - // create the dispatcher type, instantiate and initialize it - result.MethodDispatcherType = classBuilder.CreateTypeInfo().AsType(); - result.MethodDispatcher = (TMethodDispatcher)Activator.CreateInstance(result.MethodDispatcherType); - var v2MethodDispatcherBase = (ActorMethodDispatcherBase)result.MethodDispatcher; - v2MethodDispatcherBase.Initialize(interfaceDescription, methodNameMap); - - context.Complete(); - return result; - } + public MethodDispatcherBuildResult Build( + InterfaceDescription interfaceDescription) + { + var context = new CodeBuilderContext( + assemblyName: this.CodeBuilder.Names.GetMethodDispatcherAssemblyName(interfaceDescription.InterfaceType), + assemblyNamespace: this.CodeBuilder.Names.GetMethodDispatcherAssemblyNamespace(interfaceDescription + .InterfaceType), + enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(interfaceDescription.InterfaceType)); + + var result = new MethodDispatcherBuildResult(context); + + // ensure that the method body types are built + var methodBodyTypesBuildResult = + this.CodeBuilder.GetOrBuildMethodBodyTypes(interfaceDescription.InterfaceType); + + // build dispatcher class + var classBuilder = CodeBuilderUtils.CreateClassBuilder( + context.ModuleBuilder, + ns: context.AssemblyNamespace, + className: this.CodeBuilder.Names.GetMethodDispatcherClassName(interfaceDescription.InterfaceType), + baseType: this.methodDispatcherBaseType); + + this.AddCreateResponseBodyMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult); + this.AddOnDispatchAsyncMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult); + this.AddOnDispatchMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult); + + var methodNameMap = GetMethodNameMap(interfaceDescription); + + // create the dispatcher type, instantiate and initialize it + result.MethodDispatcherType = classBuilder.CreateTypeInfo().AsType(); + result.MethodDispatcher = (TMethodDispatcher)Activator.CreateInstance(result.MethodDispatcherType); + var v2MethodDispatcherBase = (ActorMethodDispatcherBase)result.MethodDispatcher; + v2MethodDispatcherBase.Initialize(interfaceDescription, methodNameMap); + + context.Complete(); + return result; + } - private static void AddIfNotWrapMsgGetParameter( - ILGenerator ilGen, - LocalBuilder castedObject, - MethodDescription methodDescription, - Type requestBody) - { - // now invoke the method on the casted object - ilGen.Emit(OpCodes.Ldloc, castedObject); + private static void AddIfNotWrapMsgGetParameter( + ILGenerator ilGen, + LocalBuilder castedObject, + MethodDescription methodDescription, + Type requestBody) + { + // now invoke the method on the casted object + ilGen.Emit(OpCodes.Ldloc, castedObject); - if ((methodDescription.Arguments != null) && (methodDescription.Arguments.Length != 0)) + if ((methodDescription.Arguments != null) && (methodDescription.Arguments.Length != 0)) + { + var method = requestBody.GetMethod("GetParameter"); + for (var i = 0; i < methodDescription.Arguments.Length; i++) { - var method = requestBody.GetMethod("GetParameter"); - for (var i = 0; i < methodDescription.Arguments.Length; i++) - { - var argument = methodDescription.Arguments[i]; - - // ReSharper disable once AssignNullToNotNullAttribute - // castedRequestBody is set to non-null in the previous if check on the same condition - ilGen.Emit(OpCodes.Ldarg_3); - ilGen.Emit(OpCodes.Ldc_I4, i); - ilGen.Emit(OpCodes.Ldstr, argument.Name); - ilGen.Emit(OpCodes.Ldtoken, argument.ArgumentType); - ilGen.Emit(OpCodes.Call, getTypeFromHandle); - ilGen.Emit(OpCodes.Callvirt, method); - ilGen.Emit(OpCodes.Unbox_Any, argument.ArgumentType); - } + var argument = methodDescription.Arguments[i]; + + // ReSharper disable once AssignNullToNotNullAttribute + // castedRequestBody is set to non-null in the previous if check on the same condition + ilGen.Emit(OpCodes.Ldarg_3); + ilGen.Emit(OpCodes.Ldc_I4, i); + ilGen.Emit(OpCodes.Ldstr, argument.Name); + ilGen.Emit(OpCodes.Ldtoken, argument.ArgumentType); + ilGen.Emit(OpCodes.Call, getTypeFromHandle); + ilGen.Emit(OpCodes.Callvirt, method); + ilGen.Emit(OpCodes.Unbox_Any, argument.ArgumentType); } } + } - private static void AddIfWrapMsgGetParameters( - ILGenerator ilGen, - LocalBuilder castedObject, - MethodBodyTypes methodBodyTypes) - { - var wrappedRequest = ilGen.DeclareLocal(typeof(object)); + private static void AddIfWrapMsgGetParameters( + ILGenerator ilGen, + LocalBuilder castedObject, + MethodBodyTypes methodBodyTypes) + { + var wrappedRequest = ilGen.DeclareLocal(typeof(object)); - var getValueMethod = typeof(WrappedMessage).GetProperty("Value").GetGetMethod(); - ilGen.Emit(OpCodes.Ldarg_3); // request object - ilGen.Emit(OpCodes.Callvirt, getValueMethod); - ilGen.Emit(OpCodes.Stloc, wrappedRequest); + var getValueMethod = typeof(WrappedMessage).GetProperty("Value").GetGetMethod(); + ilGen.Emit(OpCodes.Ldarg_3); // request object + ilGen.Emit(OpCodes.Callvirt, getValueMethod); + ilGen.Emit(OpCodes.Stloc, wrappedRequest); - // then cast and call GetField - LocalBuilder castedRequestBody = null; + // then cast and call GetField + LocalBuilder castedRequestBody = null; - if (methodBodyTypes.RequestBodyType != null) - { - // cast the request body - // var castedRequestBody = ()requestBody; - castedRequestBody = ilGen.DeclareLocal(methodBodyTypes.RequestBodyType); - ilGen.Emit(OpCodes.Ldloc, wrappedRequest); // wrapped request - ilGen.Emit(OpCodes.Castclass, methodBodyTypes.RequestBodyType); - ilGen.Emit(OpCodes.Stloc, castedRequestBody); - } + if (methodBodyTypes.RequestBodyType != null) + { + // cast the request body + // var castedRequestBody = ()requestBody; + castedRequestBody = ilGen.DeclareLocal(methodBodyTypes.RequestBodyType); + ilGen.Emit(OpCodes.Ldloc, wrappedRequest); // wrapped request + ilGen.Emit(OpCodes.Castclass, methodBodyTypes.RequestBodyType); + ilGen.Emit(OpCodes.Stloc, castedRequestBody); + } - // now invoke the method on the casted object - ilGen.Emit(OpCodes.Ldloc, castedObject); + // now invoke the method on the casted object + ilGen.Emit(OpCodes.Ldloc, castedObject); - if (methodBodyTypes.RequestBodyType != null) + if (methodBodyTypes.RequestBodyType != null) + { + foreach (var field in methodBodyTypes.RequestBodyType.GetFields()) { - foreach (var field in methodBodyTypes.RequestBodyType.GetFields()) - { - // ReSharper disable once AssignNullToNotNullAttribute - // castedRequestBody is set to non-null in the previous if check on the same condition - ilGen.Emit(OpCodes.Ldloc, castedRequestBody); - ilGen.Emit(OpCodes.Ldfld, field); - } + // ReSharper disable once AssignNullToNotNullAttribute + // castedRequestBody is set to non-null in the previous if check on the same condition + ilGen.Emit(OpCodes.Ldloc, castedRequestBody); + ilGen.Emit(OpCodes.Ldfld, field); } } + } - private void AddOnDispatchMethod( - TypeBuilder classBuilder, - InterfaceDescription interfaceDescription, - MethodBodyTypesBuildResult methodBodyTypesBuildResult) + private void AddOnDispatchMethod( + TypeBuilder classBuilder, + InterfaceDescription interfaceDescription, + MethodBodyTypesBuildResult methodBodyTypesBuildResult) + { + var dispatchMethodImpl = CodeBuilderUtils.CreateProtectedMethodBuilder( + classBuilder, + "OnDispatch", + typeof(void), + typeof(int), // methodid + typeof(object), // remoted object + typeof(IActorRequestMessageBody)); // requestBody + + var ilGen = dispatchMethodImpl.GetILGenerator(); + + var castedObject = ilGen.DeclareLocal(interfaceDescription.InterfaceType); + ilGen.Emit(OpCodes.Ldarg_2); // load remoted object + ilGen.Emit(OpCodes.Castclass, interfaceDescription.InterfaceType); + ilGen.Emit(OpCodes.Stloc, castedObject); // store casted result to local 0 + + foreach (var methodDescription in interfaceDescription.Methods) { - var dispatchMethodImpl = CodeBuilderUtils.CreateProtectedMethodBuilder( - classBuilder, - "OnDispatch", - typeof(void), - typeof(int), // methodid - typeof(object), // remoted object - typeof(IActorRequestMessageBody)); // requestBody - - var ilGen = dispatchMethodImpl.GetILGenerator(); - - var castedObject = ilGen.DeclareLocal(interfaceDescription.InterfaceType); - ilGen.Emit(OpCodes.Ldarg_2); // load remoted object - ilGen.Emit(OpCodes.Castclass, interfaceDescription.InterfaceType); - ilGen.Emit(OpCodes.Stloc, castedObject); // store casted result to local 0 - - foreach (var methodDescription in interfaceDescription.Methods) + if (!TypeUtility.IsVoidType(methodDescription.ReturnType)) { - if (!TypeUtility.IsVoidType(methodDescription.ReturnType)) - { - continue; - } + continue; + } - var elseLable = ilGen.DefineLabel(); + var elseLable = ilGen.DefineLabel(); - this.AddIfMethodIdInvokeBlock( - ilGen: ilGen, - elseLabel: elseLable, - castedObject: castedObject, - methodDescription: methodDescription, - methodBodyTypes: methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]); + this.AddIfMethodIdInvokeBlock( + ilGen: ilGen, + elseLabel: elseLable, + castedObject: castedObject, + methodDescription: methodDescription, + methodBodyTypes: methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]); - ilGen.MarkLabel(elseLable); - } - - ilGen.ThrowException(typeof(MissingMethodException)); + ilGen.MarkLabel(elseLable); } - private void AddIfMethodIdInvokeBlock( - ILGenerator ilGen, - Label elseLabel, - LocalBuilder castedObject, - MethodDescription methodDescription, - MethodBodyTypes methodBodyTypes) - { - ilGen.Emit(OpCodes.Ldarg_1); - ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); - ilGen.Emit(OpCodes.Bne_Un, elseLabel); + ilGen.ThrowException(typeof(MissingMethodException)); + } - // Check If its Wrapped , then call getparam - var requestBody = typeof(IActorRequestMessageBody); + private void AddIfMethodIdInvokeBlock( + ILGenerator ilGen, + Label elseLabel, + LocalBuilder castedObject, + MethodDescription methodDescription, + MethodBodyTypes methodBodyTypes) + { + ilGen.Emit(OpCodes.Ldarg_1); + ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); + ilGen.Emit(OpCodes.Bne_Un, elseLabel); - // now invoke the method on the casted object - ilGen.Emit(OpCodes.Ldloc, castedObject); + // Check If its Wrapped , then call getparam + var requestBody = typeof(IActorRequestMessageBody); - // Check if its WrappedMessage - var elseLabelforWrapped = ilGen.DefineLabel(); - this.AddACheckIfItsWrappedMessage(ilGen, elseLabelforWrapped); - var endlabel = ilGen.DefineLabel(); + // now invoke the method on the casted object + ilGen.Emit(OpCodes.Ldloc, castedObject); - // 2 If true then call GetValue - AddIfWrapMsgGetParameters(ilGen, castedObject, methodBodyTypes); - ilGen.Emit(OpCodes.Br, endlabel); - ilGen.MarkLabel(elseLabelforWrapped); + // Check if its WrappedMessage + var elseLabelforWrapped = ilGen.DefineLabel(); + this.AddACheckIfItsWrappedMessage(ilGen, elseLabelforWrapped); + var endlabel = ilGen.DefineLabel(); - // else call GetParameter on IServiceRemotingMessageBody - AddIfNotWrapMsgGetParameter(ilGen, castedObject, methodDescription, requestBody); + // 2 If true then call GetValue + AddIfWrapMsgGetParameters(ilGen, castedObject, methodBodyTypes); + ilGen.Emit(OpCodes.Br, endlabel); + ilGen.MarkLabel(elseLabelforWrapped); - ilGen.MarkLabel(endlabel); + // else call GetParameter on IServiceRemotingMessageBody + AddIfNotWrapMsgGetParameter(ilGen, castedObject, methodDescription, requestBody); - ilGen.EmitCall(OpCodes.Callvirt, methodDescription.MethodInfo, null); - ilGen.Emit(OpCodes.Ret); - } + ilGen.MarkLabel(endlabel); - private void AddOnDispatchAsyncMethod( - TypeBuilder classBuilder, - InterfaceDescription interfaceDescription, - MethodBodyTypesBuildResult methodBodyTypesBuildResult) + ilGen.EmitCall(OpCodes.Callvirt, methodDescription.MethodInfo, null); + ilGen.Emit(OpCodes.Ret); + } + + private void AddOnDispatchAsyncMethod( + TypeBuilder classBuilder, + InterfaceDescription interfaceDescription, + MethodBodyTypesBuildResult methodBodyTypesBuildResult) + { + var dispatchMethodImpl = CodeBuilderUtils.CreateProtectedMethodBuilder( + classBuilder, + "OnDispatchAsync", + typeof(Task), + typeof(int), // methodid + typeof(object), // remoted object + typeof(IActorRequestMessageBody), // requestBody + typeof(IActorMessageBodyFactory), // remotingmessageBodyFactory + typeof(CancellationToken)); // CancellationToken + + var ilGen = dispatchMethodImpl.GetILGenerator(); + + var castedObject = ilGen.DeclareLocal(interfaceDescription.InterfaceType); + ilGen.Emit(OpCodes.Ldarg_2); // load remoted object + ilGen.Emit(OpCodes.Castclass, interfaceDescription.InterfaceType); + ilGen.Emit(OpCodes.Stloc, castedObject); // store casted result to local 0 + + foreach (var methodDescription in interfaceDescription.Methods) { - var dispatchMethodImpl = CodeBuilderUtils.CreateProtectedMethodBuilder( - classBuilder, - "OnDispatchAsync", - typeof(Task), - typeof(int), // methodid - typeof(object), // remoted object - typeof(IActorRequestMessageBody), // requestBody - typeof(IActorMessageBodyFactory), // remotingmessageBodyFactory - typeof(CancellationToken)); // CancellationToken - - var ilGen = dispatchMethodImpl.GetILGenerator(); - - var castedObject = ilGen.DeclareLocal(interfaceDescription.InterfaceType); - ilGen.Emit(OpCodes.Ldarg_2); // load remoted object - ilGen.Emit(OpCodes.Castclass, interfaceDescription.InterfaceType); - ilGen.Emit(OpCodes.Stloc, castedObject); // store casted result to local 0 - - foreach (var methodDescription in interfaceDescription.Methods) + if (!TypeUtility.IsTaskType(methodDescription.ReturnType)) { - if (!TypeUtility.IsTaskType(methodDescription.ReturnType)) - { - continue; - } - - var elseLable = ilGen.DefineLabel(); - - this.AddIfMethodIdInvokeAsyncBlock( - ilGen: ilGen, - elseLabel: elseLable, - castedObject: castedObject, - methodDescription: methodDescription, - interfaceName: interfaceDescription.InterfaceType.FullName, - methodBodyTypes: methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]); - - ilGen.MarkLabel(elseLable); + continue; } - ilGen.ThrowException(typeof(MissingMethodException)); - } + var elseLable = ilGen.DefineLabel(); - private void AddIfMethodIdInvokeAsyncBlock( - ILGenerator ilGen, - Label elseLabel, - LocalBuilder castedObject, - MethodDescription methodDescription, - string interfaceName, - MethodBodyTypes methodBodyTypes) - { - ilGen.Emit(OpCodes.Ldarg_1); - ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); - ilGen.Emit(OpCodes.Bne_Un, elseLabel); + this.AddIfMethodIdInvokeAsyncBlock( + ilGen: ilGen, + elseLabel: elseLable, + castedObject: castedObject, + methodDescription: methodDescription, + interfaceName: interfaceDescription.InterfaceType.FullName, + methodBodyTypes: methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]); + + ilGen.MarkLabel(elseLable); + } - var invokeTask = ilGen.DeclareLocal(methodDescription.ReturnType); - var requestBody = typeof(IActorRequestMessageBody); + ilGen.ThrowException(typeof(MissingMethodException)); + } - // now invoke the method on the casted object - ilGen.Emit(OpCodes.Ldloc, castedObject); + private void AddIfMethodIdInvokeAsyncBlock( + ILGenerator ilGen, + Label elseLabel, + LocalBuilder castedObject, + MethodDescription methodDescription, + string interfaceName, + MethodBodyTypes methodBodyTypes) + { + ilGen.Emit(OpCodes.Ldarg_1); + ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); + ilGen.Emit(OpCodes.Bne_Un, elseLabel); - // Check if its WrappedMessage - var elseLabelforWrapped = ilGen.DefineLabel(); - this.AddACheckIfItsWrappedMessage(ilGen, elseLabelforWrapped); - var endlabel = ilGen.DefineLabel(); + var invokeTask = ilGen.DeclareLocal(methodDescription.ReturnType); + var requestBody = typeof(IActorRequestMessageBody); - // 2 If true then call GetValue - AddIfWrapMsgGetParameters(ilGen, castedObject, methodBodyTypes); - ilGen.Emit(OpCodes.Br, endlabel); - ilGen.MarkLabel(elseLabelforWrapped); + // now invoke the method on the casted object + ilGen.Emit(OpCodes.Ldloc, castedObject); - // else call GetParameter on IServiceRemotingMessageBody - AddIfNotWrapMsgGetParameter(ilGen, castedObject, methodDescription, requestBody); + // Check if its WrappedMessage + var elseLabelforWrapped = ilGen.DefineLabel(); + this.AddACheckIfItsWrappedMessage(ilGen, elseLabelforWrapped); + var endlabel = ilGen.DefineLabel(); - ilGen.MarkLabel(endlabel); + // 2 If true then call GetValue + AddIfWrapMsgGetParameters(ilGen, castedObject, methodBodyTypes); + ilGen.Emit(OpCodes.Br, endlabel); + ilGen.MarkLabel(elseLabelforWrapped); - if (methodDescription.HasCancellationToken) - { - ilGen.Emit(OpCodes.Ldarg, 5); - } + // else call GetParameter on IServiceRemotingMessageBody + AddIfNotWrapMsgGetParameter(ilGen, castedObject, methodDescription, requestBody); - ilGen.EmitCall(OpCodes.Callvirt, methodDescription.MethodInfo, null); - ilGen.Emit(OpCodes.Stloc, invokeTask); + ilGen.MarkLabel(endlabel); - // call the base method to return continuation task - if (TypeUtility.IsTaskType(methodDescription.ReturnType) && - methodDescription.ReturnType.GetTypeInfo().IsGenericType) - { - // the return is Task - var continueWithGenericMethodInfo = - this.continueWithResultMethodInfo.MakeGenericMethod(methodDescription.ReturnType - .GenericTypeArguments[0]); - - ilGen.Emit(OpCodes.Ldarg_0); // base - ilGen.Emit(OpCodes.Ldstr, interfaceName); - ilGen.Emit(OpCodes.Ldstr, methodDescription.Name); - ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); - ilGen.Emit(OpCodes.Ldarg, 4); // message body factory - ilGen.Emit(OpCodes.Ldloc, invokeTask); - ilGen.EmitCall(OpCodes.Call, continueWithGenericMethodInfo, null); - ilGen.Emit(OpCodes.Ret); - } - else - { - ilGen.Emit(OpCodes.Ldarg_0); // base - ilGen.Emit(OpCodes.Ldloc, invokeTask); - ilGen.EmitCall(OpCodes.Call, this.continueWithMethodInfo, null); - ilGen.Emit(OpCodes.Ret); - } + if (methodDescription.HasCancellationToken) + { + ilGen.Emit(OpCodes.Ldarg, 5); } - private void AddACheckIfItsWrappedMessage( - ILGenerator ilGen, Label elseLabelforWrapped) + ilGen.EmitCall(OpCodes.Callvirt, methodDescription.MethodInfo, null); + ilGen.Emit(OpCodes.Stloc, invokeTask); + + // call the base method to return continuation task + if (TypeUtility.IsTaskType(methodDescription.ReturnType) && + methodDescription.ReturnType.GetTypeInfo().IsGenericType) { - var boolres = ilGen.DeclareLocal(typeof(bool)); - ilGen.Emit(OpCodes.Ldarg_3); // request object - ilGen.Emit(OpCodes.Call, this.checkIfitsWrapped); - ilGen.Emit(OpCodes.Stloc, boolres); - ilGen.Emit(OpCodes.Ldloc, boolres); - ilGen.Emit(OpCodes.Brfalse, elseLabelforWrapped); + // the return is Task + var continueWithGenericMethodInfo = + this.continueWithResultMethodInfo.MakeGenericMethod(methodDescription.ReturnType + .GenericTypeArguments[0]); + + ilGen.Emit(OpCodes.Ldarg_0); // base + ilGen.Emit(OpCodes.Ldstr, interfaceName); + ilGen.Emit(OpCodes.Ldstr, methodDescription.Name); + ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); + ilGen.Emit(OpCodes.Ldarg, 4); // message body factory + ilGen.Emit(OpCodes.Ldloc, invokeTask); + ilGen.EmitCall(OpCodes.Call, continueWithGenericMethodInfo, null); + ilGen.Emit(OpCodes.Ret); } - - private void AddCreateResponseBodyMethod( - TypeBuilder classBuilder, - InterfaceDescription interfaceDescription, - MethodBodyTypesBuildResult methodBodyTypesBuildResult) + else { - var methodBuilder = CodeBuilderUtils.CreateProtectedMethodBuilder( - classBuilder, - "CreateWrappedResponseBody", - typeof(object), // responseBody - return value - typeof(int), // methodId - typeof(object)); // retval from the invoked method on the remoted object + ilGen.Emit(OpCodes.Ldarg_0); // base + ilGen.Emit(OpCodes.Ldloc, invokeTask); + ilGen.EmitCall(OpCodes.Call, this.continueWithMethodInfo, null); + ilGen.Emit(OpCodes.Ret); + } + } + + private void AddACheckIfItsWrappedMessage( + ILGenerator ilGen, Label elseLabelforWrapped) + { + var boolres = ilGen.DeclareLocal(typeof(bool)); + ilGen.Emit(OpCodes.Ldarg_3); // request object + ilGen.Emit(OpCodes.Call, this.checkIfitsWrapped); + ilGen.Emit(OpCodes.Stloc, boolres); + ilGen.Emit(OpCodes.Ldloc, boolres); + ilGen.Emit(OpCodes.Brfalse, elseLabelforWrapped); + } + + private void AddCreateResponseBodyMethod( + TypeBuilder classBuilder, + InterfaceDescription interfaceDescription, + MethodBodyTypesBuildResult methodBodyTypesBuildResult) + { + var methodBuilder = CodeBuilderUtils.CreateProtectedMethodBuilder( + classBuilder, + "CreateWrappedResponseBody", + typeof(object), // responseBody - return value + typeof(int), // methodId + typeof(object)); // retval from the invoked method on the remoted object - var ilGen = methodBuilder.GetILGenerator(); + var ilGen = methodBuilder.GetILGenerator(); - foreach (var methodDescription in interfaceDescription.Methods) + foreach (var methodDescription in interfaceDescription.Methods) + { + var methodBodyTypes = methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]; + if (methodBodyTypes.ResponseBodyType == null) { - var methodBodyTypes = methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]; - if (methodBodyTypes.ResponseBodyType == null) - { - continue; - } + continue; + } - var elseLabel = ilGen.DefineLabel(); + var elseLabel = ilGen.DefineLabel(); - this.AddIfMethodIdCreateResponseBlock( - ilGen, - elseLabel, - methodDescription.Id, - methodBodyTypes.ResponseBodyType); + this.AddIfMethodIdCreateResponseBlock( + ilGen, + elseLabel, + methodDescription.Id, + methodBodyTypes.ResponseBodyType); - ilGen.MarkLabel(elseLabel); - } + ilGen.MarkLabel(elseLabel); + } - // return null; (if method id's do not match) - ilGen.Emit(OpCodes.Ldnull); + // return null; (if method id's do not match) + ilGen.Emit(OpCodes.Ldnull); + ilGen.Emit(OpCodes.Ret); + } + + private void AddIfMethodIdCreateResponseBlock( + ILGenerator ilGen, + Label elseLabel, + int methodId, + Type responseType) + { + // if (methodId == ) + ilGen.Emit(OpCodes.Ldarg_1); + ilGen.Emit(OpCodes.Ldc_I4, methodId); + ilGen.Emit(OpCodes.Bne_Un, elseLabel); + + var ctorInfo = responseType.GetConstructor(Type.EmptyTypes); + if (ctorInfo != null) + { + var localBuilder = ilGen.DeclareLocal(responseType); + + // new + ilGen.Emit(OpCodes.Newobj, ctorInfo); + ilGen.Emit(OpCodes.Stloc, localBuilder); + ilGen.Emit(OpCodes.Ldloc, localBuilder); + + // responseBody.retval = ()retval; + var fInfo = responseType.GetField(this.CodeBuilder.Names.RetVal); + ilGen.Emit(OpCodes.Ldarg_2); + ilGen.Emit( + fInfo.FieldType.GetTypeInfo().IsClass ? OpCodes.Castclass : OpCodes.Unbox_Any, + fInfo.FieldType); + ilGen.Emit(OpCodes.Stfld, fInfo); + ilGen.Emit(OpCodes.Ldloc, localBuilder); ilGen.Emit(OpCodes.Ret); } - - private void AddIfMethodIdCreateResponseBlock( - ILGenerator ilGen, - Label elseLabel, - int methodId, - Type responseType) + else { - // if (methodId == ) - ilGen.Emit(OpCodes.Ldarg_1); - ilGen.Emit(OpCodes.Ldc_I4, methodId); - ilGen.Emit(OpCodes.Bne_Un, elseLabel); - - var ctorInfo = responseType.GetConstructor(Type.EmptyTypes); - if (ctorInfo != null) - { - var localBuilder = ilGen.DeclareLocal(responseType); - - // new - ilGen.Emit(OpCodes.Newobj, ctorInfo); - ilGen.Emit(OpCodes.Stloc, localBuilder); - ilGen.Emit(OpCodes.Ldloc, localBuilder); - - // responseBody.retval = ()retval; - var fInfo = responseType.GetField(this.CodeBuilder.Names.RetVal); - ilGen.Emit(OpCodes.Ldarg_2); - ilGen.Emit( - fInfo.FieldType.GetTypeInfo().IsClass ? OpCodes.Castclass : OpCodes.Unbox_Any, - fInfo.FieldType); - ilGen.Emit(OpCodes.Stfld, fInfo); - ilGen.Emit(OpCodes.Ldloc, localBuilder); - ilGen.Emit(OpCodes.Ret); - } - else - { - ilGen.Emit(OpCodes.Ldnull); - ilGen.Emit(OpCodes.Ret); - } + ilGen.Emit(OpCodes.Ldnull); + ilGen.Emit(OpCodes.Ret); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Client/ActorProxy.cs b/src/Dapr.Actors/Client/ActorProxy.cs index 841e3b093..ae0243674 100644 --- a/src/Dapr.Actors/Client/ActorProxy.cs +++ b/src/Dapr.Actors/Client/ActorProxy.cs @@ -11,310 +11,309 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Client -{ - using System; - using System.IO; - using System.Text; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Communication; - using Dapr.Actors.Communication.Client; +namespace Dapr.Actors.Client; + +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Communication; +using Dapr.Actors.Communication.Client; +/// +/// Provides the base implementation for the proxy to the remote actor objects implementing interfaces. +/// The proxy object can be used for client-to-actor and actor-to-actor communication. +/// +public class ActorProxy : IActorProxy +{ /// - /// Provides the base implementation for the proxy to the remote actor objects implementing interfaces. - /// The proxy object can be used for client-to-actor and actor-to-actor communication. + /// The default factory used to create an actor proxy /// - public class ActorProxy : IActorProxy - { - /// - /// The default factory used to create an actor proxy - /// - public static IActorProxyFactory DefaultProxyFactory { get; } = new ActorProxyFactory(); + public static IActorProxyFactory DefaultProxyFactory { get; } = new ActorProxyFactory(); - private ActorRemotingClient actorRemotingClient; - private ActorNonRemotingClient actorNonRemotingClient; + private ActorRemotingClient actorRemotingClient; + private ActorNonRemotingClient actorNonRemotingClient; - /// - /// Initializes a new instance of the class. - /// This constructor is protected so that it can be used by generated class which derives from ActorProxy when making Remoting calls. - /// This constructor is also marked as internal so that it can be called by ActorProxyFactory when making non-remoting calls. - /// - protected internal ActorProxy() - { - } + /// + /// Initializes a new instance of the class. + /// This constructor is protected so that it can be used by generated class which derives from ActorProxy when making Remoting calls. + /// This constructor is also marked as internal so that it can be called by ActorProxyFactory when making non-remoting calls. + /// + protected internal ActorProxy() + { + } - /// - public ActorId ActorId { get; private set; } + /// + public ActorId ActorId { get; private set; } - /// - public string ActorType { get; private set; } + /// + public string ActorType { get; private set; } - internal IActorMessageBodyFactory ActorMessageBodyFactory { get; set; } - internal JsonSerializerOptions JsonSerializerOptions { get; set; } - internal string DaprApiToken; + internal IActorMessageBodyFactory ActorMessageBodyFactory { get; set; } + internal JsonSerializerOptions JsonSerializerOptions { get; set; } + internal string DaprApiToken; - /// - /// Creates a proxy to the actor object that implements an actor interface. - /// - /// - /// The actor interface implemented by the remote actor object. - /// The returned proxy object will implement this interface. - /// - /// The actor ID of the proxy actor object. Methods called on this proxy will result in requests - /// being sent to the actor with this ID. - /// Type of actor implementation. - /// The optional to use when creating the actor proxy. - /// Proxy to the actor object. - public static TActorInterface Create(ActorId actorId, string actorType, ActorProxyOptions options = null) - where TActorInterface : IActor - { - return DefaultProxyFactory.CreateActorProxy(actorId, actorType, options); - } + /// + /// Creates a proxy to the actor object that implements an actor interface. + /// + /// + /// The actor interface implemented by the remote actor object. + /// The returned proxy object will implement this interface. + /// + /// The actor ID of the proxy actor object. Methods called on this proxy will result in requests + /// being sent to the actor with this ID. + /// Type of actor implementation. + /// The optional to use when creating the actor proxy. + /// Proxy to the actor object. + public static TActorInterface Create(ActorId actorId, string actorType, ActorProxyOptions options = null) + where TActorInterface : IActor + { + return DefaultProxyFactory.CreateActorProxy(actorId, actorType, options); + } - /// - /// Creates a proxy to the actor object that implements an actor interface. - /// - /// The actor ID of the proxy actor object. Methods called on this proxy will result in requests - /// being sent to the actor with this ID. - /// - /// The actor interface type implemented by the remote actor object. - /// The returned proxy object will implement this interface. - /// - /// Type of actor implementation. - /// The optional to use when creating the actor proxy. - /// Proxy to the actor object. - public static object Create(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null) + /// + /// Creates a proxy to the actor object that implements an actor interface. + /// + /// The actor ID of the proxy actor object. Methods called on this proxy will result in requests + /// being sent to the actor with this ID. + /// + /// The actor interface type implemented by the remote actor object. + /// The returned proxy object will implement this interface. + /// + /// Type of actor implementation. + /// The optional to use when creating the actor proxy. + /// Proxy to the actor object. + public static object Create(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null) + { + if (!typeof(IActor).IsAssignableFrom(actorInterfaceType)) { - if (!typeof(IActor).IsAssignableFrom(actorInterfaceType)) - { - throw new ArgumentException("The interface must implement IActor.", nameof(actorInterfaceType)); - } - return DefaultProxyFactory.CreateActorProxy(actorId, actorInterfaceType, actorType, options); + throw new ArgumentException("The interface must implement IActor.", nameof(actorInterfaceType)); } + return DefaultProxyFactory.CreateActorProxy(actorId, actorInterfaceType, actorType, options); + } - /// - /// Creates an Actor Proxy for making calls without Remoting. - /// - /// Actor Id. - /// Type of actor. - /// The optional to use when creating the actor proxy. - /// Actor proxy to interact with remote actor object. - public static ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null) - { - return DefaultProxyFactory.Create(actorId, actorType, options); - } + /// + /// Creates an Actor Proxy for making calls without Remoting. + /// + /// Actor Id. + /// Type of actor. + /// The optional to use when creating the actor proxy. + /// Actor proxy to interact with remote actor object. + public static ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null) + { + return DefaultProxyFactory.Create(actorId, actorType, options); + } - /// - /// Invokes the specified method for the actor with argument. The argument will be serialized as JSON. - /// - /// The data type of the object that will be serialized. - /// Return type of method. - /// Actor method name. - /// Object argument for actor method. - /// Cancellation Token. - /// Response form server. - public async Task InvokeMethodAsync(string method, TRequest data, CancellationToken cancellationToken = default) - { - using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, data, JsonSerializerOptions); - await stream.FlushAsync(); - var jsonPayload = Encoding.UTF8.GetString(stream.ToArray()); - var response = await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, jsonPayload, cancellationToken); - return await JsonSerializer.DeserializeAsync(response, JsonSerializerOptions); - } + /// + /// Invokes the specified method for the actor with argument. The argument will be serialized as JSON. + /// + /// The data type of the object that will be serialized. + /// Return type of method. + /// Actor method name. + /// Object argument for actor method. + /// Cancellation Token. + /// Response form server. + public async Task InvokeMethodAsync(string method, TRequest data, CancellationToken cancellationToken = default) + { + using var stream = new MemoryStream(); + await JsonSerializer.SerializeAsync(stream, data, JsonSerializerOptions); + await stream.FlushAsync(); + var jsonPayload = Encoding.UTF8.GetString(stream.ToArray()); + var response = await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, jsonPayload, cancellationToken); + return await JsonSerializer.DeserializeAsync(response, JsonSerializerOptions); + } - /// - /// Invokes the specified method for the actor with argument. The argument will be serialized as JSON. - /// - /// The data type of the object that will be serialized. - /// Actor method name. - /// Object argument for actor method. - /// Cancellation Token. - /// Response form server. - public async Task InvokeMethodAsync(string method, TRequest data, CancellationToken cancellationToken = default) - { - using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, data, JsonSerializerOptions); - await stream.FlushAsync(); - var jsonPayload = Encoding.UTF8.GetString(stream.ToArray()); - await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, jsonPayload, cancellationToken); - } + /// + /// Invokes the specified method for the actor with argument. The argument will be serialized as JSON. + /// + /// The data type of the object that will be serialized. + /// Actor method name. + /// Object argument for actor method. + /// Cancellation Token. + /// Response form server. + public async Task InvokeMethodAsync(string method, TRequest data, CancellationToken cancellationToken = default) + { + using var stream = new MemoryStream(); + await JsonSerializer.SerializeAsync(stream, data, JsonSerializerOptions); + await stream.FlushAsync(); + var jsonPayload = Encoding.UTF8.GetString(stream.ToArray()); + await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, jsonPayload, cancellationToken); + } - /// - /// Invokes the specified method for the actor with argument. - /// - /// Return type of method. - /// Actor method name. - /// Cancellation Token. - /// Response form server. - public async Task InvokeMethodAsync(string method, CancellationToken cancellationToken = default) - { - var response = await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, null, cancellationToken); - return await JsonSerializer.DeserializeAsync(response, JsonSerializerOptions); - } + /// + /// Invokes the specified method for the actor with argument. + /// + /// Return type of method. + /// Actor method name. + /// Cancellation Token. + /// Response form server. + public async Task InvokeMethodAsync(string method, CancellationToken cancellationToken = default) + { + var response = await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, null, cancellationToken); + return await JsonSerializer.DeserializeAsync(response, JsonSerializerOptions); + } - /// - /// Invokes the specified method for the actor with argument. - /// - /// Actor method name. - /// Cancellation Token. - /// Response form server. - public Task InvokeMethodAsync(string method, CancellationToken cancellationToken = default) - { - return this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, null, cancellationToken); - } + /// + /// Invokes the specified method for the actor with argument. + /// + /// Actor method name. + /// Cancellation Token. + /// Response form server. + public Task InvokeMethodAsync(string method, CancellationToken cancellationToken = default) + { + return this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, null, cancellationToken); + } - /// - /// Initialize when ActorProxy is created for Remoting. - /// - internal void Initialize( - ActorRemotingClient client, - ActorId actorId, - string actorType, - ActorProxyOptions options) - { - this.actorRemotingClient = client; - this.ActorId = actorId; - this.ActorType = actorType; - this.ActorMessageBodyFactory = client.GetRemotingMessageBodyFactory(); - this.JsonSerializerOptions = options?.JsonSerializerOptions ?? new JsonSerializerOptions(JsonSerializerDefaults.Web); - this.DaprApiToken = options?.DaprApiToken; - } + /// + /// Initialize when ActorProxy is created for Remoting. + /// + internal void Initialize( + ActorRemotingClient client, + ActorId actorId, + string actorType, + ActorProxyOptions options) + { + this.actorRemotingClient = client; + this.ActorId = actorId; + this.ActorType = actorType; + this.ActorMessageBodyFactory = client.GetRemotingMessageBodyFactory(); + this.JsonSerializerOptions = options?.JsonSerializerOptions ?? new JsonSerializerOptions(JsonSerializerDefaults.Web); + this.DaprApiToken = options?.DaprApiToken; + } - /// - /// Initialize when ActorProxy is created for non-Remoting calls. - /// - internal void Initialize( - ActorNonRemotingClient client, - ActorId actorId, - string actorType, - ActorProxyOptions options) - { - this.actorNonRemotingClient = client; - this.ActorId = actorId; - this.ActorType = actorType; - this.JsonSerializerOptions = options?.JsonSerializerOptions ?? this.JsonSerializerOptions; - } + /// + /// Initialize when ActorProxy is created for non-Remoting calls. + /// + internal void Initialize( + ActorNonRemotingClient client, + ActorId actorId, + string actorType, + ActorProxyOptions options) + { + this.actorNonRemotingClient = client; + this.ActorId = actorId; + this.ActorType = actorType; + this.JsonSerializerOptions = options?.JsonSerializerOptions ?? this.JsonSerializerOptions; + } - /// - /// Invokes the specified method for the actor with provided request. - /// - /// Interface ID. - /// Method ID. - /// Method Name. - /// Request Message Body Value. - /// Cancellation Token. - /// A representing the result of the asynchronous operation. - protected async Task InvokeMethodAsync( - int interfaceId, - int methodId, - string methodName, - IActorRequestMessageBody requestMsgBodyValue, - CancellationToken cancellationToken) + /// + /// Invokes the specified method for the actor with provided request. + /// + /// Interface ID. + /// Method ID. + /// Method Name. + /// Request Message Body Value. + /// Cancellation Token. + /// A representing the result of the asynchronous operation. + protected async Task InvokeMethodAsync( + int interfaceId, + int methodId, + string methodName, + IActorRequestMessageBody requestMsgBodyValue, + CancellationToken cancellationToken) + { + var headers = new ActorRequestMessageHeader { - var headers = new ActorRequestMessageHeader - { - ActorId = this.ActorId, - ActorType = this.ActorType, - InterfaceId = interfaceId, - MethodId = methodId, - CallContext = Actors.Helper.GetCallContext(), - MethodName = methodName, - }; + ActorId = this.ActorId, + ActorType = this.ActorType, + InterfaceId = interfaceId, + MethodId = methodId, + CallContext = Actors.Helper.GetCallContext(), + MethodName = methodName, + }; - var responseMsg = await this.actorRemotingClient.InvokeAsync( - new ActorRequestMessage( - headers, - requestMsgBodyValue), - cancellationToken); + var responseMsg = await this.actorRemotingClient.InvokeAsync( + new ActorRequestMessage( + headers, + requestMsgBodyValue), + cancellationToken); - return responseMsg?.GetBody(); - } + return responseMsg?.GetBody(); + } - /// - /// Creates the Actor request message Body. - /// - /// Full Name of the service interface for which this call is invoked. - /// Method Name of the service interface for which this call is invoked. - /// Number of Parameters in the service interface Method. - /// Wrapped Request Object. - /// A request message body. - protected IActorRequestMessageBody CreateRequestMessageBody( - string interfaceName, - string methodName, - int parameterCount, - object wrappedRequest) - { - return this.ActorMessageBodyFactory.CreateRequestMessageBody(interfaceName, methodName, parameterCount, wrappedRequest); - } + /// + /// Creates the Actor request message Body. + /// + /// Full Name of the service interface for which this call is invoked. + /// Method Name of the service interface for which this call is invoked. + /// Number of Parameters in the service interface Method. + /// Wrapped Request Object. + /// A request message body. + protected IActorRequestMessageBody CreateRequestMessageBody( + string interfaceName, + string methodName, + int parameterCount, + object wrappedRequest) + { + return this.ActorMessageBodyFactory.CreateRequestMessageBody(interfaceName, methodName, parameterCount, wrappedRequest); + } - /// - /// This method is used by the generated proxy type and should be used directly. This method converts the Task with object - /// return value to a Task without the return value for the void method invocation. - /// - /// A task returned from the method that contains null return value. - /// A task that represents the asynchronous operation for remote method call without the return value. - protected Task ContinueWith(Task task) - { - return task; - } + /// + /// This method is used by the generated proxy type and should be used directly. This method converts the Task with object + /// return value to a Task without the return value for the void method invocation. + /// + /// A task returned from the method that contains null return value. + /// A task that represents the asynchronous operation for remote method call without the return value. + protected Task ContinueWith(Task task) + { + return task; + } - /// - /// This method is used by the generated proxy type and should be used directly. This method converts the Task with object - /// return value to a Task without the return value for the void method invocation. - /// - /// Interface Id for the actor interface. - /// Method Id for the actor method. - /// Response body. - /// Return value of method call as . - protected virtual object GetReturnValue(int interfaceId, int methodId, object responseBody) - { - return Task.CompletedTask; - } + /// + /// This method is used by the generated proxy type and should be used directly. This method converts the Task with object + /// return value to a Task without the return value for the void method invocation. + /// + /// Interface Id for the actor interface. + /// Method Id for the actor method. + /// Response body. + /// Return value of method call as . + protected virtual object GetReturnValue(int interfaceId, int methodId, object responseBody) + { + return Task.CompletedTask; + } - /// - /// Called by the generated proxy class to get the result from the response body. - /// - /// of the remote method return value. - /// InterfaceId of the remoting interface. - /// MethodId of the remoting Method. - /// A task that represents the asynchronous operation for remote method call. - /// A task that represents the asynchronous operation for remote method call. - /// The value of the TRetval contains the remote method return value. - protected async Task ContinueWithResult( - int interfaceId, - int methodId, - Task task) + /// + /// Called by the generated proxy class to get the result from the response body. + /// + /// of the remote method return value. + /// InterfaceId of the remoting interface. + /// MethodId of the remoting Method. + /// A task that represents the asynchronous operation for remote method call. + /// A task that represents the asynchronous operation for remote method call. + /// The value of the TRetval contains the remote method return value. + protected async Task ContinueWithResult( + int interfaceId, + int methodId, + Task task) + { + var responseBody = await task; + if (responseBody is WrappedMessage wrappedMessage) { - var responseBody = await task; - if (responseBody is WrappedMessage wrappedMessage) - { - var obj = this.GetReturnValue( - interfaceId, - methodId, - wrappedMessage.Value); - - return (TRetval)obj; - } + var obj = this.GetReturnValue( + interfaceId, + methodId, + wrappedMessage.Value); - return (TRetval)responseBody.Get(typeof(TRetval)); + return (TRetval)obj; } - /// - /// This check if we are wrapping actor message or not. - /// - /// Actor Request Message Body. - /// true or false. - protected bool CheckIfItsWrappedRequest(IActorRequestMessageBody requestMessageBody) - { - if (requestMessageBody is WrappedMessage) - { - return true; - } + return (TRetval)responseBody.Get(typeof(TRetval)); + } - return false; + /// + /// This check if we are wrapping actor message or not. + /// + /// Actor Request Message Body. + /// true or false. + protected bool CheckIfItsWrappedRequest(IActorRequestMessageBody requestMessageBody) + { + if (requestMessageBody is WrappedMessage) + { + return true; } + + return false; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Client/ActorProxyFactory.cs b/src/Dapr.Actors/Client/ActorProxyFactory.cs index 4a8fe3a08..9b4452135 100644 --- a/src/Dapr.Actors/Client/ActorProxyFactory.cs +++ b/src/Dapr.Actors/Client/ActorProxyFactory.cs @@ -11,89 +11,88 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Client +namespace Dapr.Actors.Client; + +using System; +using System.Net.Http; +using Dapr.Actors.Builder; +using Dapr.Actors.Communication; +using Dapr.Actors.Communication.Client; + +/// +/// Represents a factory class to create a proxy to the remote actor objects. +/// +public class ActorProxyFactory : IActorProxyFactory { - using System; - using System.Net.Http; - using Dapr.Actors.Builder; - using Dapr.Actors.Communication; - using Dapr.Actors.Communication.Client; + private ActorProxyOptions defaultOptions; + private readonly HttpMessageHandler handler; - /// - /// Represents a factory class to create a proxy to the remote actor objects. - /// - public class ActorProxyFactory : IActorProxyFactory + /// + public ActorProxyOptions DefaultOptions { - private ActorProxyOptions defaultOptions; - private readonly HttpMessageHandler handler; - - /// - public ActorProxyOptions DefaultOptions + get => this.defaultOptions; + set { - get => this.defaultOptions; - set - { - this.defaultOptions = value ?? - throw new ArgumentNullException(nameof(DefaultOptions), $"{nameof(ActorProxyFactory)}.{nameof(DefaultOptions)} cannot be null"); - } + this.defaultOptions = value ?? + throw new ArgumentNullException(nameof(DefaultOptions), $"{nameof(ActorProxyFactory)}.{nameof(DefaultOptions)} cannot be null"); } + } - /// - /// Initializes a new instance of the class. - /// - [Obsolete("Use the constructor that accepts HttpMessageHandler. This will be removed in the future.")] - public ActorProxyFactory(ActorProxyOptions options, HttpClientHandler handler) - : this(options, (HttpMessageHandler)handler) - { - } + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Use the constructor that accepts HttpMessageHandler. This will be removed in the future.")] + public ActorProxyFactory(ActorProxyOptions options, HttpClientHandler handler) + : this(options, (HttpMessageHandler)handler) + { + } - /// - /// Initializes a new instance of the class. - /// - public ActorProxyFactory(ActorProxyOptions options = null, HttpMessageHandler handler = null) - { - this.defaultOptions = options ?? new ActorProxyOptions(); - this.handler = handler; - } + /// + /// Initializes a new instance of the class. + /// + public ActorProxyFactory(ActorProxyOptions options = null, HttpMessageHandler handler = null) + { + this.defaultOptions = options ?? new ActorProxyOptions(); + this.handler = handler; + } - /// - public TActorInterface CreateActorProxy(ActorId actorId, string actorType, ActorProxyOptions options = null) - where TActorInterface : IActor - => (TActorInterface)this.CreateActorProxy(actorId, typeof(TActorInterface), actorType, options ?? this.defaultOptions); + /// + public TActorInterface CreateActorProxy(ActorId actorId, string actorType, ActorProxyOptions options = null) + where TActorInterface : IActor + => (TActorInterface)this.CreateActorProxy(actorId, typeof(TActorInterface), actorType, options ?? this.defaultOptions); - /// - public ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null) - { - options ??= this.DefaultOptions; + /// + public ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null) + { + options ??= this.DefaultOptions; - var actorProxy = new ActorProxy(); - var daprInteractor = new DaprHttpInteractor(this.handler, options.HttpEndpoint, options.DaprApiToken, options.RequestTimeout); - var nonRemotingClient = new ActorNonRemotingClient(daprInteractor); - actorProxy.Initialize(nonRemotingClient, actorId, actorType, options); + var actorProxy = new ActorProxy(); + var daprInteractor = new DaprHttpInteractor(this.handler, options.HttpEndpoint, options.DaprApiToken, options.RequestTimeout); + var nonRemotingClient = new ActorNonRemotingClient(daprInteractor); + actorProxy.Initialize(nonRemotingClient, actorId, actorType, options); - return actorProxy; - } + return actorProxy; + } - /// - public object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null) - { - options ??= this.DefaultOptions; + /// + public object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null) + { + options ??= this.DefaultOptions; - var daprInteractor = new DaprHttpInteractor(this.handler, options.HttpEndpoint, options.DaprApiToken, options.RequestTimeout); + var daprInteractor = new DaprHttpInteractor(this.handler, options.HttpEndpoint, options.DaprApiToken, options.RequestTimeout); - // provide a serializer if 'useJsonSerialization' is true and no serialization provider is provided. - IActorMessageBodySerializationProvider serializationProvider = null; - if (options.UseJsonSerialization) - { - serializationProvider = new ActorMessageBodyJsonSerializationProvider(options.JsonSerializerOptions); - } + // provide a serializer if 'useJsonSerialization' is true and no serialization provider is provided. + IActorMessageBodySerializationProvider serializationProvider = null; + if (options.UseJsonSerialization) + { + serializationProvider = new ActorMessageBodyJsonSerializationProvider(options.JsonSerializerOptions); + } - var remotingClient = new ActorRemotingClient(daprInteractor, serializationProvider); - var proxyGenerator = ActorCodeBuilder.GetOrCreateProxyGenerator(actorInterfaceType); - var actorProxy = proxyGenerator.CreateActorProxy(); - actorProxy.Initialize(remotingClient, actorId, actorType, options); + var remotingClient = new ActorRemotingClient(daprInteractor, serializationProvider); + var proxyGenerator = ActorCodeBuilder.GetOrCreateProxyGenerator(actorInterfaceType); + var actorProxy = proxyGenerator.CreateActorProxy(); + actorProxy.Initialize(remotingClient, actorId, actorType, options); - return actorProxy; - } + return actorProxy; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Client/ActorProxyOptions.cs b/src/Dapr.Actors/Client/ActorProxyOptions.cs index 2afce852c..6ad6cf1eb 100644 --- a/src/Dapr.Actors/Client/ActorProxyOptions.cs +++ b/src/Dapr.Actors/Client/ActorProxyOptions.cs @@ -11,61 +11,60 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Client +namespace Dapr.Actors.Client; + +using System; +using System.Text.Json; + +/// +/// The class containing customizable options for how the Actor Proxy is initialized. +/// +public class ActorProxyOptions { - using System; - using System.Text.Json; + // TODO: Add actor retry settings + + private JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); /// - /// The class containing customizable options for how the Actor Proxy is initialized. + /// The constructor /// - public class ActorProxyOptions + public ActorProxyOptions() { - // TODO: Add actor retry settings - - private JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); - - /// - /// The constructor - /// - public ActorProxyOptions() - { - } + } - /// - /// The used for actor proxy message serialization in non-remoting invocation. - /// - public JsonSerializerOptions JsonSerializerOptions - { - get => this.jsonSerializerOptions; - set => this.jsonSerializerOptions = value ?? - throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorProxyOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); - } + /// + /// The used for actor proxy message serialization in non-remoting invocation. + /// + public JsonSerializerOptions JsonSerializerOptions + { + get => this.jsonSerializerOptions; + set => this.jsonSerializerOptions = value ?? + throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorProxyOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); + } - /// - /// The Dapr Api Token that is added to the header for all requests. - /// - public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(null); + /// + /// The Dapr Api Token that is added to the header for all requests. + /// + public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(null); - /// - /// Gets or sets the HTTP endpoint URI used to communicate with the Dapr sidecar. - /// - /// - /// The URI endpoint to use for HTTP calls to the Dapr runtime. The default value will be - /// http://127.0.0.1:DAPR_HTTP_PORT where DAPR_HTTP_PORT represents the value of the - /// DAPR_HTTP_PORT environment variable. - /// - /// - public string HttpEndpoint { get; set; } = DaprDefaults.GetDefaultHttpEndpoint(); + /// + /// Gets or sets the HTTP endpoint URI used to communicate with the Dapr sidecar. + /// + /// + /// The URI endpoint to use for HTTP calls to the Dapr runtime. The default value will be + /// http://127.0.0.1:DAPR_HTTP_PORT where DAPR_HTTP_PORT represents the value of the + /// DAPR_HTTP_PORT environment variable. + /// + /// + public string HttpEndpoint { get; set; } = DaprDefaults.GetDefaultHttpEndpoint(); - /// - /// The timeout allowed for an actor request. Can be set to System.Threading.Timeout.InfiniteTimeSpan to disable any timeouts. - /// - public TimeSpan? RequestTimeout { get; set; } = null; + /// + /// The timeout allowed for an actor request. Can be set to System.Threading.Timeout.InfiniteTimeSpan to disable any timeouts. + /// + public TimeSpan? RequestTimeout { get; set; } = null; - /// - /// Enable JSON serialization for actor proxy message serialization in both remoting and non-remoting invocations. - /// - public bool UseJsonSerialization { get; set; } - } -} + /// + /// Enable JSON serialization for actor proxy message serialization in both remoting and non-remoting invocations. + /// + public bool UseJsonSerialization { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Client/IActorProxy.cs b/src/Dapr.Actors/Client/IActorProxy.cs index 9d24b47a0..d2991617a 100644 --- a/src/Dapr.Actors/Client/IActorProxy.cs +++ b/src/Dapr.Actors/Client/IActorProxy.cs @@ -11,23 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Client +namespace Dapr.Actors.Client; + +/// +/// Provides the interface for implementation of proxy access for actor service. +/// +public interface IActorProxy { /// - /// Provides the interface for implementation of proxy access for actor service. + /// Gets associated with the proxy object. /// - public interface IActorProxy - { - /// - /// Gets associated with the proxy object. - /// - /// associated with the proxy object. - ActorId ActorId { get; } + /// associated with the proxy object. + ActorId ActorId { get; } - /// - /// Gets actor implementation type of the actor associated with the proxy object. - /// - /// Actor implementation type of the actor associated with the proxy object. - string ActorType { get; } - } -} + /// + /// Gets actor implementation type of the actor associated with the proxy object. + /// + /// Actor implementation type of the actor associated with the proxy object. + string ActorType { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Client/IActorProxyFactory.cs b/src/Dapr.Actors/Client/IActorProxyFactory.cs index 81af4eecb..a2e7692e6 100644 --- a/src/Dapr.Actors/Client/IActorProxyFactory.cs +++ b/src/Dapr.Actors/Client/IActorProxyFactory.cs @@ -13,48 +13,47 @@ using System; -namespace Dapr.Actors.Client +namespace Dapr.Actors.Client; + +/// +/// Defines the interface containing methods to create actor proxy factory class. +/// +public interface IActorProxyFactory { /// - /// Defines the interface containing methods to create actor proxy factory class. + /// Creates a proxy to the actor object that implements an actor interface. /// - public interface IActorProxyFactory - { - /// - /// Creates a proxy to the actor object that implements an actor interface. - /// - /// - /// The actor interface implemented by the remote actor object. - /// The returned proxy object will implement this interface. - /// - /// Actor Id of the proxy actor object. Methods called on this proxy will result in requests - /// being sent to the actor with this id. - /// Type of actor implementation. - /// The optional to use when creating the actor proxy. - /// An actor proxy object that implements IActorProxy and TActorInterface. - TActorInterface CreateActorProxy( - ActorId actorId, - string actorType, - ActorProxyOptions options = null) - where TActorInterface : IActor; + /// + /// The actor interface implemented by the remote actor object. + /// The returned proxy object will implement this interface. + /// + /// Actor Id of the proxy actor object. Methods called on this proxy will result in requests + /// being sent to the actor with this id. + /// Type of actor implementation. + /// The optional to use when creating the actor proxy. + /// An actor proxy object that implements IActorProxy and TActorInterface. + TActorInterface CreateActorProxy( + ActorId actorId, + string actorType, + ActorProxyOptions options = null) + where TActorInterface : IActor; - /// - /// Create a proxy, this method is also used by ActorReference also to create proxy. - /// - /// Actor Id. - /// Actor Interface Type. - /// Actor implementation Type. - /// The optional to use when creating the actor proxy. - /// Returns Actor Proxy. - object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null); + /// + /// Create a proxy, this method is also used by ActorReference also to create proxy. + /// + /// Actor Id. + /// Actor Interface Type. + /// Actor implementation Type. + /// The optional to use when creating the actor proxy. + /// Returns Actor Proxy. + object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null); - /// - /// Creates an Actor Proxy for making calls without Remoting. - /// - /// Actor Id. - /// Type of actor. - /// The optional to use when creating the actor proxy. - /// Actor proxy to interact with remote actor object. - ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null); - } -} + /// + /// Creates an Actor Proxy for making calls without Remoting. + /// + /// Actor Id. + /// Type of actor. + /// The optional to use when creating the actor proxy. + /// Actor proxy to interact with remote actor object. + ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null); +} \ No newline at end of file diff --git a/src/Dapr.Actors/ClientSettings.cs b/src/Dapr.Actors/ClientSettings.cs index ce179ff4f..912f17b09 100644 --- a/src/Dapr.Actors/ClientSettings.cs +++ b/src/Dapr.Actors/ClientSettings.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors -{ - using System; +namespace Dapr.Actors; + +using System; +/// +/// Represents connection settings for Http/gRPC Client to interact with Dapr runtime. +/// +internal class ClientSettings +{ /// - /// Represents connection settings for Http/gRPC Client to interact with Dapr runtime. + /// Initializes a new instance of the class. /// - internal class ClientSettings + /// Timespan to wait before the request times out for the client. + public ClientSettings(TimeSpan? clientTimeout = null) { - /// - /// Initializes a new instance of the class. - /// - /// Timespan to wait before the request times out for the client. - public ClientSettings(TimeSpan? clientTimeout = null) - { - this.ClientTimeout = clientTimeout; - } - - /// - /// Gets or sets the Timespan to wait before the request times out for the client. - /// - public TimeSpan? ClientTimeout { get; set; } + this.ClientTimeout = clientTimeout; } -} + + /// + /// Gets or sets the Timespan to wait before the request times out for the client. + /// + public TimeSpan? ClientTimeout { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Common/CRC64.cs b/src/Dapr.Actors/Common/CRC64.cs index 5470d1258..4f164aebd 100644 --- a/src/Dapr.Actors/Common/CRC64.cs +++ b/src/Dapr.Actors/Common/CRC64.cs @@ -11,132 +11,131 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Common +namespace Dapr.Actors.Common; + +using System.Globalization; + +/// +/// Computes CRC64 for a given byte payload. +/// +internal static class CRC64 { - using System.Globalization; + /// + /// CRC table. + /// + private static readonly ulong[] Crc64Table = + { + 0x0000000000000000, 0x42F0E1EBA9EA3693, 0x85E1C3D753D46D26, 0xC711223CFA3E5BB5, + 0x493366450E42ECDF, 0x0BC387AEA7A8DA4C, 0xCCD2A5925D9681F9, 0x8E224479F47CB76A, + 0x9266CC8A1C85D9BE, 0xD0962D61B56FEF2D, 0x17870F5D4F51B498, 0x5577EEB6E6BB820B, + 0xDB55AACF12C73561, 0x99A54B24BB2D03F2, 0x5EB4691841135847, 0x1C4488F3E8F96ED4, + 0x663D78FF90E185EF, 0x24CD9914390BB37C, 0xE3DCBB28C335E8C9, 0xA12C5AC36ADFDE5A, + 0x2F0E1EBA9EA36930, 0x6DFEFF5137495FA3, 0xAAEFDD6DCD770416, 0xE81F3C86649D3285, + 0xF45BB4758C645C51, 0xB6AB559E258E6AC2, 0x71BA77A2DFB03177, 0x334A9649765A07E4, + 0xBD68D2308226B08E, 0xFF9833DB2BCC861D, 0x388911E7D1F2DDA8, 0x7A79F00C7818EB3B, + 0xCC7AF1FF21C30BDE, 0x8E8A101488293D4D, 0x499B3228721766F8, 0x0B6BD3C3DBFD506B, + 0x854997BA2F81E701, 0xC7B97651866BD192, 0x00A8546D7C558A27, 0x4258B586D5BFBCB4, + 0x5E1C3D753D46D260, 0x1CECDC9E94ACE4F3, 0xDBFDFEA26E92BF46, 0x990D1F49C77889D5, + 0x172F5B3033043EBF, 0x55DFBADB9AEE082C, 0x92CE98E760D05399, 0xD03E790CC93A650A, + 0xAA478900B1228E31, 0xE8B768EB18C8B8A2, 0x2FA64AD7E2F6E317, 0x6D56AB3C4B1CD584, + 0xE374EF45BF6062EE, 0xA1840EAE168A547D, 0x66952C92ECB40FC8, 0x2465CD79455E395B, + 0x3821458AADA7578F, 0x7AD1A461044D611C, 0xBDC0865DFE733AA9, 0xFF3067B657990C3A, + 0x711223CFA3E5BB50, 0x33E2C2240A0F8DC3, 0xF4F3E018F031D676, 0xB60301F359DBE0E5, + 0xDA050215EA6C212F, 0x98F5E3FE438617BC, 0x5FE4C1C2B9B84C09, 0x1D14202910527A9A, + 0x93366450E42ECDF0, 0xD1C685BB4DC4FB63, 0x16D7A787B7FAA0D6, 0x5427466C1E109645, + 0x4863CE9FF6E9F891, 0x0A932F745F03CE02, 0xCD820D48A53D95B7, 0x8F72ECA30CD7A324, + 0x0150A8DAF8AB144E, 0x43A04931514122DD, 0x84B16B0DAB7F7968, 0xC6418AE602954FFB, + 0xBC387AEA7A8DA4C0, 0xFEC89B01D3679253, 0x39D9B93D2959C9E6, 0x7B2958D680B3FF75, + 0xF50B1CAF74CF481F, 0xB7FBFD44DD257E8C, 0x70EADF78271B2539, 0x321A3E938EF113AA, + 0x2E5EB66066087D7E, 0x6CAE578BCFE24BED, 0xABBF75B735DC1058, 0xE94F945C9C3626CB, + 0x676DD025684A91A1, 0x259D31CEC1A0A732, 0xE28C13F23B9EFC87, 0xA07CF2199274CA14, + 0x167FF3EACBAF2AF1, 0x548F120162451C62, 0x939E303D987B47D7, 0xD16ED1D631917144, + 0x5F4C95AFC5EDC62E, 0x1DBC74446C07F0BD, 0xDAAD56789639AB08, 0x985DB7933FD39D9B, + 0x84193F60D72AF34F, 0xC6E9DE8B7EC0C5DC, 0x01F8FCB784FE9E69, 0x43081D5C2D14A8FA, + 0xCD2A5925D9681F90, 0x8FDAB8CE70822903, 0x48CB9AF28ABC72B6, 0x0A3B7B1923564425, + 0x70428B155B4EAF1E, 0x32B26AFEF2A4998D, 0xF5A348C2089AC238, 0xB753A929A170F4AB, + 0x3971ED50550C43C1, 0x7B810CBBFCE67552, 0xBC902E8706D82EE7, 0xFE60CF6CAF321874, + 0xE224479F47CB76A0, 0xA0D4A674EE214033, 0x67C58448141F1B86, 0x253565A3BDF52D15, + 0xAB1721DA49899A7F, 0xE9E7C031E063ACEC, 0x2EF6E20D1A5DF759, 0x6C0603E6B3B7C1CA, + 0xF6FAE5C07D3274CD, 0xB40A042BD4D8425E, 0x731B26172EE619EB, 0x31EBC7FC870C2F78, + 0xBFC9838573709812, 0xFD39626EDA9AAE81, 0x3A28405220A4F534, 0x78D8A1B9894EC3A7, + 0x649C294A61B7AD73, 0x266CC8A1C85D9BE0, 0xE17DEA9D3263C055, 0xA38D0B769B89F6C6, + 0x2DAF4F0F6FF541AC, 0x6F5FAEE4C61F773F, 0xA84E8CD83C212C8A, 0xEABE6D3395CB1A19, + 0x90C79D3FEDD3F122, 0xD2377CD44439C7B1, 0x15265EE8BE079C04, 0x57D6BF0317EDAA97, + 0xD9F4FB7AE3911DFD, 0x9B041A914A7B2B6E, 0x5C1538ADB04570DB, 0x1EE5D94619AF4648, + 0x02A151B5F156289C, 0x4051B05E58BC1E0F, 0x87409262A28245BA, 0xC5B073890B687329, + 0x4B9237F0FF14C443, 0x0962D61B56FEF2D0, 0xCE73F427ACC0A965, 0x8C8315CC052A9FF6, + 0x3A80143F5CF17F13, 0x7870F5D4F51B4980, 0xBF61D7E80F251235, 0xFD913603A6CF24A6, + 0x73B3727A52B393CC, 0x31439391FB59A55F, 0xF652B1AD0167FEEA, 0xB4A25046A88DC879, + 0xA8E6D8B54074A6AD, 0xEA16395EE99E903E, 0x2D071B6213A0CB8B, 0x6FF7FA89BA4AFD18, + 0xE1D5BEF04E364A72, 0xA3255F1BE7DC7CE1, 0x64347D271DE22754, 0x26C49CCCB40811C7, + 0x5CBD6CC0CC10FAFC, 0x1E4D8D2B65FACC6F, 0xD95CAF179FC497DA, 0x9BAC4EFC362EA149, + 0x158E0A85C2521623, 0x577EEB6E6BB820B0, 0x906FC95291867B05, 0xD29F28B9386C4D96, + 0xCEDBA04AD0952342, 0x8C2B41A1797F15D1, 0x4B3A639D83414E64, 0x09CA82762AAB78F7, + 0x87E8C60FDED7CF9D, 0xC51827E4773DF90E, 0x020905D88D03A2BB, 0x40F9E43324E99428, + 0x2CFFE7D5975E55E2, 0x6E0F063E3EB46371, 0xA91E2402C48A38C4, 0xEBEEC5E96D600E57, + 0x65CC8190991CB93D, 0x273C607B30F68FAE, 0xE02D4247CAC8D41B, 0xA2DDA3AC6322E288, + 0xBE992B5F8BDB8C5C, 0xFC69CAB42231BACF, 0x3B78E888D80FE17A, 0x7988096371E5D7E9, + 0xF7AA4D1A85996083, 0xB55AACF12C735610, 0x724B8ECDD64D0DA5, 0x30BB6F267FA73B36, + 0x4AC29F2A07BFD00D, 0x08327EC1AE55E69E, 0xCF235CFD546BBD2B, 0x8DD3BD16FD818BB8, + 0x03F1F96F09FD3CD2, 0x41011884A0170A41, 0x86103AB85A2951F4, 0xC4E0DB53F3C36767, + 0xD8A453A01B3A09B3, 0x9A54B24BB2D03F20, 0x5D45907748EE6495, 0x1FB5719CE1045206, + 0x919735E51578E56C, 0xD367D40EBC92D3FF, 0x1476F63246AC884A, 0x568617D9EF46BED9, + 0xE085162AB69D5E3C, 0xA275F7C11F7768AF, 0x6564D5FDE549331A, 0x279434164CA30589, + 0xA9B6706FB8DFB2E3, 0xEB46918411358470, 0x2C57B3B8EB0BDFC5, 0x6EA7525342E1E956, + 0x72E3DAA0AA188782, 0x30133B4B03F2B111, 0xF7021977F9CCEAA4, 0xB5F2F89C5026DC37, + 0x3BD0BCE5A45A6B5D, 0x79205D0E0DB05DCE, 0xBE317F32F78E067B, 0xFCC19ED95E6430E8, + 0x86B86ED5267CDBD3, 0xC4488F3E8F96ED40, 0x0359AD0275A8B6F5, 0x41A94CE9DC428066, + 0xCF8B0890283E370C, 0x8D7BE97B81D4019F, 0x4A6ACB477BEA5A2A, 0x089A2AACD2006CB9, + 0x14DEA25F3AF9026D, 0x562E43B4931334FE, 0x913F6188692D6F4B, 0xD3CF8063C0C759D8, + 0x5DEDC41A34BBEEB2, 0x1F1D25F19D51D821, 0xD80C07CD676F8394, 0x9AFCE626CE85B507, + }; /// - /// Computes CRC64 for a given byte payload. + /// Returns the CRC64 for the given payload. /// - internal static class CRC64 + /// Byte payload. + /// CRC64 value. + public static ulong ToCRC64(byte[] value) { - /// - /// CRC table. - /// - private static readonly ulong[] Crc64Table = + var crc = 0xffffffffffffffff; + for (var i = 0; i < value.Length; i++) { - 0x0000000000000000, 0x42F0E1EBA9EA3693, 0x85E1C3D753D46D26, 0xC711223CFA3E5BB5, - 0x493366450E42ECDF, 0x0BC387AEA7A8DA4C, 0xCCD2A5925D9681F9, 0x8E224479F47CB76A, - 0x9266CC8A1C85D9BE, 0xD0962D61B56FEF2D, 0x17870F5D4F51B498, 0x5577EEB6E6BB820B, - 0xDB55AACF12C73561, 0x99A54B24BB2D03F2, 0x5EB4691841135847, 0x1C4488F3E8F96ED4, - 0x663D78FF90E185EF, 0x24CD9914390BB37C, 0xE3DCBB28C335E8C9, 0xA12C5AC36ADFDE5A, - 0x2F0E1EBA9EA36930, 0x6DFEFF5137495FA3, 0xAAEFDD6DCD770416, 0xE81F3C86649D3285, - 0xF45BB4758C645C51, 0xB6AB559E258E6AC2, 0x71BA77A2DFB03177, 0x334A9649765A07E4, - 0xBD68D2308226B08E, 0xFF9833DB2BCC861D, 0x388911E7D1F2DDA8, 0x7A79F00C7818EB3B, - 0xCC7AF1FF21C30BDE, 0x8E8A101488293D4D, 0x499B3228721766F8, 0x0B6BD3C3DBFD506B, - 0x854997BA2F81E701, 0xC7B97651866BD192, 0x00A8546D7C558A27, 0x4258B586D5BFBCB4, - 0x5E1C3D753D46D260, 0x1CECDC9E94ACE4F3, 0xDBFDFEA26E92BF46, 0x990D1F49C77889D5, - 0x172F5B3033043EBF, 0x55DFBADB9AEE082C, 0x92CE98E760D05399, 0xD03E790CC93A650A, - 0xAA478900B1228E31, 0xE8B768EB18C8B8A2, 0x2FA64AD7E2F6E317, 0x6D56AB3C4B1CD584, - 0xE374EF45BF6062EE, 0xA1840EAE168A547D, 0x66952C92ECB40FC8, 0x2465CD79455E395B, - 0x3821458AADA7578F, 0x7AD1A461044D611C, 0xBDC0865DFE733AA9, 0xFF3067B657990C3A, - 0x711223CFA3E5BB50, 0x33E2C2240A0F8DC3, 0xF4F3E018F031D676, 0xB60301F359DBE0E5, - 0xDA050215EA6C212F, 0x98F5E3FE438617BC, 0x5FE4C1C2B9B84C09, 0x1D14202910527A9A, - 0x93366450E42ECDF0, 0xD1C685BB4DC4FB63, 0x16D7A787B7FAA0D6, 0x5427466C1E109645, - 0x4863CE9FF6E9F891, 0x0A932F745F03CE02, 0xCD820D48A53D95B7, 0x8F72ECA30CD7A324, - 0x0150A8DAF8AB144E, 0x43A04931514122DD, 0x84B16B0DAB7F7968, 0xC6418AE602954FFB, - 0xBC387AEA7A8DA4C0, 0xFEC89B01D3679253, 0x39D9B93D2959C9E6, 0x7B2958D680B3FF75, - 0xF50B1CAF74CF481F, 0xB7FBFD44DD257E8C, 0x70EADF78271B2539, 0x321A3E938EF113AA, - 0x2E5EB66066087D7E, 0x6CAE578BCFE24BED, 0xABBF75B735DC1058, 0xE94F945C9C3626CB, - 0x676DD025684A91A1, 0x259D31CEC1A0A732, 0xE28C13F23B9EFC87, 0xA07CF2199274CA14, - 0x167FF3EACBAF2AF1, 0x548F120162451C62, 0x939E303D987B47D7, 0xD16ED1D631917144, - 0x5F4C95AFC5EDC62E, 0x1DBC74446C07F0BD, 0xDAAD56789639AB08, 0x985DB7933FD39D9B, - 0x84193F60D72AF34F, 0xC6E9DE8B7EC0C5DC, 0x01F8FCB784FE9E69, 0x43081D5C2D14A8FA, - 0xCD2A5925D9681F90, 0x8FDAB8CE70822903, 0x48CB9AF28ABC72B6, 0x0A3B7B1923564425, - 0x70428B155B4EAF1E, 0x32B26AFEF2A4998D, 0xF5A348C2089AC238, 0xB753A929A170F4AB, - 0x3971ED50550C43C1, 0x7B810CBBFCE67552, 0xBC902E8706D82EE7, 0xFE60CF6CAF321874, - 0xE224479F47CB76A0, 0xA0D4A674EE214033, 0x67C58448141F1B86, 0x253565A3BDF52D15, - 0xAB1721DA49899A7F, 0xE9E7C031E063ACEC, 0x2EF6E20D1A5DF759, 0x6C0603E6B3B7C1CA, - 0xF6FAE5C07D3274CD, 0xB40A042BD4D8425E, 0x731B26172EE619EB, 0x31EBC7FC870C2F78, - 0xBFC9838573709812, 0xFD39626EDA9AAE81, 0x3A28405220A4F534, 0x78D8A1B9894EC3A7, - 0x649C294A61B7AD73, 0x266CC8A1C85D9BE0, 0xE17DEA9D3263C055, 0xA38D0B769B89F6C6, - 0x2DAF4F0F6FF541AC, 0x6F5FAEE4C61F773F, 0xA84E8CD83C212C8A, 0xEABE6D3395CB1A19, - 0x90C79D3FEDD3F122, 0xD2377CD44439C7B1, 0x15265EE8BE079C04, 0x57D6BF0317EDAA97, - 0xD9F4FB7AE3911DFD, 0x9B041A914A7B2B6E, 0x5C1538ADB04570DB, 0x1EE5D94619AF4648, - 0x02A151B5F156289C, 0x4051B05E58BC1E0F, 0x87409262A28245BA, 0xC5B073890B687329, - 0x4B9237F0FF14C443, 0x0962D61B56FEF2D0, 0xCE73F427ACC0A965, 0x8C8315CC052A9FF6, - 0x3A80143F5CF17F13, 0x7870F5D4F51B4980, 0xBF61D7E80F251235, 0xFD913603A6CF24A6, - 0x73B3727A52B393CC, 0x31439391FB59A55F, 0xF652B1AD0167FEEA, 0xB4A25046A88DC879, - 0xA8E6D8B54074A6AD, 0xEA16395EE99E903E, 0x2D071B6213A0CB8B, 0x6FF7FA89BA4AFD18, - 0xE1D5BEF04E364A72, 0xA3255F1BE7DC7CE1, 0x64347D271DE22754, 0x26C49CCCB40811C7, - 0x5CBD6CC0CC10FAFC, 0x1E4D8D2B65FACC6F, 0xD95CAF179FC497DA, 0x9BAC4EFC362EA149, - 0x158E0A85C2521623, 0x577EEB6E6BB820B0, 0x906FC95291867B05, 0xD29F28B9386C4D96, - 0xCEDBA04AD0952342, 0x8C2B41A1797F15D1, 0x4B3A639D83414E64, 0x09CA82762AAB78F7, - 0x87E8C60FDED7CF9D, 0xC51827E4773DF90E, 0x020905D88D03A2BB, 0x40F9E43324E99428, - 0x2CFFE7D5975E55E2, 0x6E0F063E3EB46371, 0xA91E2402C48A38C4, 0xEBEEC5E96D600E57, - 0x65CC8190991CB93D, 0x273C607B30F68FAE, 0xE02D4247CAC8D41B, 0xA2DDA3AC6322E288, - 0xBE992B5F8BDB8C5C, 0xFC69CAB42231BACF, 0x3B78E888D80FE17A, 0x7988096371E5D7E9, - 0xF7AA4D1A85996083, 0xB55AACF12C735610, 0x724B8ECDD64D0DA5, 0x30BB6F267FA73B36, - 0x4AC29F2A07BFD00D, 0x08327EC1AE55E69E, 0xCF235CFD546BBD2B, 0x8DD3BD16FD818BB8, - 0x03F1F96F09FD3CD2, 0x41011884A0170A41, 0x86103AB85A2951F4, 0xC4E0DB53F3C36767, - 0xD8A453A01B3A09B3, 0x9A54B24BB2D03F20, 0x5D45907748EE6495, 0x1FB5719CE1045206, - 0x919735E51578E56C, 0xD367D40EBC92D3FF, 0x1476F63246AC884A, 0x568617D9EF46BED9, - 0xE085162AB69D5E3C, 0xA275F7C11F7768AF, 0x6564D5FDE549331A, 0x279434164CA30589, - 0xA9B6706FB8DFB2E3, 0xEB46918411358470, 0x2C57B3B8EB0BDFC5, 0x6EA7525342E1E956, - 0x72E3DAA0AA188782, 0x30133B4B03F2B111, 0xF7021977F9CCEAA4, 0xB5F2F89C5026DC37, - 0x3BD0BCE5A45A6B5D, 0x79205D0E0DB05DCE, 0xBE317F32F78E067B, 0xFCC19ED95E6430E8, - 0x86B86ED5267CDBD3, 0xC4488F3E8F96ED40, 0x0359AD0275A8B6F5, 0x41A94CE9DC428066, - 0xCF8B0890283E370C, 0x8D7BE97B81D4019F, 0x4A6ACB477BEA5A2A, 0x089A2AACD2006CB9, - 0x14DEA25F3AF9026D, 0x562E43B4931334FE, 0x913F6188692D6F4B, 0xD3CF8063C0C759D8, - 0x5DEDC41A34BBEEB2, 0x1F1D25F19D51D821, 0xD80C07CD676F8394, 0x9AFCE626CE85B507, - }; + var tableIndex = (((uint)(crc >> 56)) ^ value[i]) & 0xff; + crc = Crc64Table[tableIndex] ^ (crc << 8); + } - /// - /// Returns the CRC64 for the given payload. - /// - /// Byte payload. - /// CRC64 value. - public static ulong ToCRC64(byte[] value) + return crc ^ 0xffffffffffffffff; + } + + /// + /// Returns the CRC64 for the given payload. + /// + /// Byte payloads. + /// CRC64 value. + public static ulong ToCRC64(params byte[][] values) + { + var crc = 0xffffffffffffffff; + for (var x = 0; x < values.Length; x++) { - var crc = 0xffffffffffffffff; - for (var i = 0; i < value.Length; i++) + for (var i = 0; i < values[x].Length; i++) { - var tableIndex = (((uint)(crc >> 56)) ^ value[i]) & 0xff; + var tableIndex = (((uint)(crc >> 56)) ^ values[x][i]) & 0xff; crc = Crc64Table[tableIndex] ^ (crc << 8); } - - return crc ^ 0xffffffffffffffff; } - /// - /// Returns the CRC64 for the given payload. - /// - /// Byte payloads. - /// CRC64 value. - public static ulong ToCRC64(params byte[][] values) - { - var crc = 0xffffffffffffffff; - for (var x = 0; x < values.Length; x++) - { - for (var i = 0; i < values[x].Length; i++) - { - var tableIndex = (((uint)(crc >> 56)) ^ values[x][i]) & 0xff; - crc = Crc64Table[tableIndex] ^ (crc << 8); - } - } - - return crc ^ 0xffffffffffffffff; - } + return crc ^ 0xffffffffffffffff; + } - /// - /// Returns the CRC64 in string form for the given payload. - /// - /// Byte payload. - /// CRC64 value. - public static string ToCrc64String(byte[] value) - { - var crc64 = ToCRC64(value); - return crc64.ToString("X", CultureInfo.InvariantCulture); - } + /// + /// Returns the CRC64 in string form for the given payload. + /// + /// Byte payload. + /// CRC64 value. + public static string ToCrc64String(byte[] value) + { + var crc64 = ToCRC64(value); + return crc64.ToString("X", CultureInfo.InvariantCulture); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Common/IdUtil.cs b/src/Dapr.Actors/Common/IdUtil.cs index 7a5092fca..0d353697c 100644 --- a/src/Dapr.Actors/Common/IdUtil.cs +++ b/src/Dapr.Actors/Common/IdUtil.cs @@ -11,95 +11,94 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Common -{ - using System; - using System.Reflection; - using System.Text; +namespace Dapr.Actors.Common; + +using System; +using System.Reflection; +using System.Text; - internal static class IdUtil +internal static class IdUtil +{ + internal static int ComputeId(MethodInfo methodInfo) { - internal static int ComputeId(MethodInfo methodInfo) - { - var hash = methodInfo.Name.GetHashCode(); + var hash = methodInfo.Name.GetHashCode(); - if (methodInfo.DeclaringType != null) + if (methodInfo.DeclaringType != null) + { + if (methodInfo.DeclaringType.Namespace != null) { - if (methodInfo.DeclaringType.Namespace != null) - { - hash = HashCombine(methodInfo.DeclaringType.Namespace.GetHashCode(), hash); - } - - hash = HashCombine(methodInfo.DeclaringType.Name.GetHashCode(), hash); + hash = HashCombine(methodInfo.DeclaringType.Namespace.GetHashCode(), hash); } - return hash; + hash = HashCombine(methodInfo.DeclaringType.Name.GetHashCode(), hash); } - internal static int ComputeId(Type type) - { - var hash = type.Name.GetHashCode(); - if (type.Namespace != null) - { - hash = HashCombine(type.Namespace.GetHashCode(), hash); - } + return hash; + } - return hash; + internal static int ComputeId(Type type) + { + var hash = type.Name.GetHashCode(); + if (type.Namespace != null) + { + hash = HashCombine(type.Namespace.GetHashCode(), hash); } - internal static int ComputeIdWithCRC(Type type) - { - var name = type.Name; + return hash; + } - if (type.Namespace != null) - { - name = string.Concat(type.Namespace, name); - } + internal static int ComputeIdWithCRC(Type type) + { + var name = type.Name; - return ComputeIdWithCRC(name); + if (type.Namespace != null) + { + name = string.Concat(type.Namespace, name); } - internal static int ComputeIdWithCRC(MethodInfo methodInfo) - { - var name = methodInfo.Name; + return ComputeIdWithCRC(name); + } - if (methodInfo.DeclaringType != null) - { - if (methodInfo.DeclaringType.Namespace != null) - { - name = string.Concat(methodInfo.DeclaringType.Namespace, name); - } + internal static int ComputeIdWithCRC(MethodInfo methodInfo) + { + var name = methodInfo.Name; - name = string.Concat(methodInfo.DeclaringType.Name, name); + if (methodInfo.DeclaringType != null) + { + if (methodInfo.DeclaringType.Namespace != null) + { + name = string.Concat(methodInfo.DeclaringType.Namespace, name); } - return ComputeIdWithCRC(name); + name = string.Concat(methodInfo.DeclaringType.Name, name); } - internal static int ComputeIdWithCRC(string typeName) - { - return (int)CRC64.ToCRC64(Encoding.UTF8.GetBytes(typeName)); - } + return ComputeIdWithCRC(name); + } - internal static int ComputeId(string typeName, string typeNamespace) - { - var hash = typeName.GetHashCode(); - if (typeNamespace != null) - { - hash = HashCombine(typeNamespace.GetHashCode(), hash); - } + internal static int ComputeIdWithCRC(string typeName) + { + return (int)CRC64.ToCRC64(Encoding.UTF8.GetBytes(typeName)); + } - return hash; + internal static int ComputeId(string typeName, string typeNamespace) + { + var hash = typeName.GetHashCode(); + if (typeNamespace != null) + { + hash = HashCombine(typeNamespace.GetHashCode(), hash); } - /// - /// This is how VB Anonymous Types combine hash values for fields. - /// - internal static int HashCombine(int newKey, int currentKey) - { + return hash; + } + + /// + /// This is how VB Anonymous Types combine hash values for fields. + /// + internal static int HashCombine(int newKey, int currentKey) + { #pragma warning disable SA1139 // Use literal suffix notation instead of casting - return unchecked((currentKey * (int)0xA5555529) + newKey); + return unchecked((currentKey * (int)0xA5555529) + newKey); #pragma warning restore SA1139 // Use literal suffix notation instead of casting - } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorDataContractSurrogate.cs b/src/Dapr.Actors/Communication/ActorDataContractSurrogate.cs index b957a9b87..e83d3eb81 100644 --- a/src/Dapr.Actors/Communication/ActorDataContractSurrogate.cs +++ b/src/Dapr.Actors/Communication/ActorDataContractSurrogate.cs @@ -11,53 +11,52 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System; +using System.Runtime.Serialization; + +internal class ActorDataContractSurrogate : ISerializationSurrogateProvider { - using System; - using System.Runtime.Serialization; + public static readonly ISerializationSurrogateProvider Instance = new ActorDataContractSurrogate(); - internal class ActorDataContractSurrogate : ISerializationSurrogateProvider + public Type GetSurrogateType(Type type) { - public static readonly ISerializationSurrogateProvider Instance = new ActorDataContractSurrogate(); - - public Type GetSurrogateType(Type type) + if (typeof(IActor).IsAssignableFrom(type)) { - if (typeof(IActor).IsAssignableFrom(type)) - { - return typeof(ActorReference); - } - - return type; + return typeof(ActorReference); } - public object GetObjectToSerialize(object obj, Type targetType) + return type; + } + + public object GetObjectToSerialize(object obj, Type targetType) + { + if (obj == null) { - if (obj == null) - { - return null; - } - else if (obj is IActor) - { - return ActorReference.Get(obj); - } - - return obj; + return null; } + else if (obj is IActor) + { + return ActorReference.Get(obj); + } + + return obj; + } - public object GetDeserializedObject(object obj, Type targetType) + public object GetDeserializedObject(object obj, Type targetType) + { + if (obj == null) { - if (obj == null) - { - return null; - } - else if (obj is IActorReference reference && - typeof(IActor).IsAssignableFrom(targetType) && - !typeof(IActorReference).IsAssignableFrom(targetType)) - { - return reference.Bind(targetType); - } - - return obj; + return null; } + else if (obj is IActorReference reference && + typeof(IActor).IsAssignableFrom(targetType) && + !typeof(IActorReference).IsAssignableFrom(targetType)) + { + return reference.Bind(targetType); + } + + return obj; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorInvokeException.cs b/src/Dapr.Actors/Communication/ActorInvokeException.cs index d58ceace8..1c9e00f02 100644 --- a/src/Dapr.Actors/Communication/ActorInvokeException.cs +++ b/src/Dapr.Actors/Communication/ActorInvokeException.cs @@ -11,88 +11,87 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors +namespace Dapr.Actors; + +using System; +using System.IO; +using Microsoft.Extensions.Logging; + +/// +/// Provides information about an exception from the actor service. +/// +public class ActorInvokeException : Exception { - using System; - using System.IO; - using Microsoft.Extensions.Logging; + /// + /// Initializes a new instance of the class. + /// + public ActorInvokeException() + { + } /// - /// Provides information about an exception from the actor service. + /// Initializes a new instance of the class with appropriate message. /// - public class ActorInvokeException : Exception + /// The fully-qualified name of the exception type thrown by the actor. + /// The error message that explains the reason for this exception. + /// + public ActorInvokeException(string actualExceptionType, string message) + : base(message) { - /// - /// Initializes a new instance of the class. - /// - public ActorInvokeException() - { - } + this.ActualExceptionType = actualExceptionType; + } - /// - /// Initializes a new instance of the class with appropriate message. - /// - /// The fully-qualified name of the exception type thrown by the actor. - /// The error message that explains the reason for this exception. - /// - public ActorInvokeException(string actualExceptionType, string message) - : base(message) - { - this.ActualExceptionType = actualExceptionType; - } + /// + /// Gets the fully-qualified name of the exception type thrown by the actor. + /// + public string ActualExceptionType { get; private set; } - /// - /// Gets the fully-qualified name of the exception type thrown by the actor. - /// - public string ActualExceptionType { get; private set; } + /// + /// Factory method that constructs the ActorInvokeException from an exception. + /// + /// Exception. + /// Serialized bytes. + internal static byte[] FromException(Exception exception) + { + var exceptionData = new ActorInvokeExceptionData(exception.GetType().FullName, exception.Message); + var exceptionBytes = exceptionData.Serialize(); - /// - /// Factory method that constructs the ActorInvokeException from an exception. - /// - /// Exception. - /// Serialized bytes. - internal static byte[] FromException(Exception exception) - { - var exceptionData = new ActorInvokeExceptionData(exception.GetType().FullName, exception.Message); - var exceptionBytes = exceptionData.Serialize(); + return exceptionBytes; + } - return exceptionBytes; + /// + /// Gets the exception from the ActorInvokeException. + /// + /// The stream that contains the serialized exception or exception message. + /// Exception from the remote side. + /// true if there was a valid exception, false otherwise. + internal static bool ToException(Stream stream, out Exception result) + { + // try to de-serialize the bytes in to exception requestMessage and create service exception + if (ActorInvokeException.TryDeserialize(stream, out result)) + { + return true; } - /// - /// Gets the exception from the ActorInvokeException. - /// - /// The stream that contains the serialized exception or exception message. - /// Exception from the remote side. - /// true if there was a valid exception, false otherwise. - internal static bool ToException(Stream stream, out Exception result) - { - // try to de-serialize the bytes in to exception requestMessage and create service exception - if (ActorInvokeException.TryDeserialize(stream, out result)) - { - return true; - } + return false; + } - return false; + internal static bool TryDeserialize(Stream stream, out Exception result, ILogger logger = null) + { + try + { + stream.Seek(0, SeekOrigin.Begin); + var eData = ActorInvokeExceptionData.Deserialize(stream); + result = new ActorInvokeException(eData.Type, eData.Message); + return true; } - - internal static bool TryDeserialize(Stream stream, out Exception result, ILogger logger = null) + catch (Exception e) { - try - { - stream.Seek(0, SeekOrigin.Begin); - var eData = ActorInvokeExceptionData.Deserialize(stream); - result = new ActorInvokeException(eData.Type, eData.Message); - return true; - } - catch (Exception e) - { - // swallowing the exception - logger?.LogWarning("RemoteException: DeSerialization failed : Reason {0}", e); - } - - result = null; - return false; + // swallowing the exception + logger?.LogWarning("RemoteException: DeSerialization failed : Reason {0}", e); } + + result = null; + return false; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs b/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs index 6103c79c4..093487685 100644 --- a/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs +++ b/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs @@ -11,47 +11,46 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors +namespace Dapr.Actors; + +using System.IO; +using System.Runtime.Serialization; +using System.Xml; + +[DataContract(Name = "ActorInvokeExceptionData", Namespace = Constants.Namespace)] +internal class ActorInvokeExceptionData { - using System.IO; - using System.Runtime.Serialization; - using System.Xml; + private static readonly DataContractSerializer ActorInvokeExceptionDataSerializer = new DataContractSerializer(typeof(ActorInvokeExceptionData)); - [DataContract(Name = "ActorInvokeExceptionData", Namespace = Constants.Namespace)] - internal class ActorInvokeExceptionData + public ActorInvokeExceptionData(string type, string message) { - private static readonly DataContractSerializer ActorInvokeExceptionDataSerializer = new DataContractSerializer(typeof(ActorInvokeExceptionData)); - - public ActorInvokeExceptionData(string type, string message) - { - this.Type = type; - this.Message = message; - } + this.Type = type; + this.Message = message; + } - [DataMember] - public string Type { get; private set; } + [DataMember] + public string Type { get; private set; } - [DataMember] - public string Message { get; private set; } + [DataMember] + public string Message { get; private set; } - internal static ActorInvokeExceptionData Deserialize(Stream stream) + internal static ActorInvokeExceptionData Deserialize(Stream stream) + { + if ((stream == null) || (stream.Length == 0)) { - if ((stream == null) || (stream.Length == 0)) - { - return null; - } - - using var reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max); - return (ActorInvokeExceptionData)ActorInvokeExceptionDataSerializer.ReadObject(reader); + return null; } - internal byte[] Serialize() - { - using var stream = new MemoryStream(); - using var writer = XmlDictionaryWriter.CreateBinaryWriter(stream); - ActorInvokeExceptionDataSerializer.WriteObject(writer, this); - writer.Flush(); - return stream.ToArray(); - } + using var reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max); + return (ActorInvokeExceptionData)ActorInvokeExceptionDataSerializer.ReadObject(reader); + } + + internal byte[] Serialize() + { + using var stream = new MemoryStream(); + using var writer = XmlDictionaryWriter.CreateBinaryWriter(stream); + ActorInvokeExceptionDataSerializer.WriteObject(writer, this); + writer.Flush(); + return stream.ToArray(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorLogicalCallContext.cs b/src/Dapr.Actors/Communication/ActorLogicalCallContext.cs index ab9cc4552..c34708876 100644 --- a/src/Dapr.Actors/Communication/ActorLogicalCallContext.cs +++ b/src/Dapr.Actors/Communication/ActorLogicalCallContext.cs @@ -11,33 +11,32 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System.Threading; + +internal static class ActorLogicalCallContext { - using System.Threading; + private static readonly AsyncLocal fabActAsyncLocal = new AsyncLocal(); - internal static class ActorLogicalCallContext + public static bool IsPresent() { - private static readonly AsyncLocal fabActAsyncLocal = new AsyncLocal(); - - public static bool IsPresent() - { - return (fabActAsyncLocal.Value != null); - } + return (fabActAsyncLocal.Value != null); + } - public static bool TryGet(out string callContextValue) - { - callContextValue = fabActAsyncLocal.Value; - return (callContextValue != null); - } + public static bool TryGet(out string callContextValue) + { + callContextValue = fabActAsyncLocal.Value; + return (callContextValue != null); + } - public static void Set(string callContextValue) - { - fabActAsyncLocal.Value = callContextValue; - } + public static void Set(string callContextValue) + { + fabActAsyncLocal.Value = callContextValue; + } - public static void Clear() - { - fabActAsyncLocal.Value = null; - } + public static void Clear() + { + fabActAsyncLocal.Value = null; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorMessageBodyDataContractSerializationProvider.cs b/src/Dapr.Actors/Communication/ActorMessageBodyDataContractSerializationProvider.cs index cf16ee2d8..687cd84df 100644 --- a/src/Dapr.Actors/Communication/ActorMessageBodyDataContractSerializationProvider.cs +++ b/src/Dapr.Actors/Communication/ActorMessageBodyDataContractSerializationProvider.cs @@ -11,93 +11,232 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using System.Xml; + +/// +/// This is the implmentation for used by remoting service and client during +/// request/response serialization . It uses request Wrapping and data contract for serialization. +/// +internal class ActorMessageBodyDataContractSerializationProvider : IActorMessageBodySerializationProvider { - using System; - using System.Collections.Generic; - using System.IO; - using System.Runtime.Serialization; - using System.Threading.Tasks; - using System.Xml; + private static readonly IEnumerable DefaultKnownTypes = new[] + { + typeof(ActorReference), + }; + + /// + /// Initializes a new instance of the class. + /// + public ActorMessageBodyDataContractSerializationProvider() + { + } + + /// + /// Creates a MessageFactory for Wrapped Message DataContract Remoting Types. This is used to create Remoting Request/Response objects. + /// + /// + /// that provides an instance of the factory for creating + /// remoting request and response message bodies. + /// + public IActorMessageBodyFactory CreateMessageBodyFactory() + { + return new WrappedRequestMessageFactory(); + } + + /// + /// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. + /// + /// The remoted service interface. + /// The union of parameter types of all of the methods of the specified interface. + /// Wrapped Request Types for all Methods. + /// + /// An instance of the that can serialize the service + /// actor request message body to a messaging body for transferring over the transport. + /// + public IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer( + Type serviceInterfaceType, + IEnumerable methodRequestParameterTypes, + IEnumerable wrappedRequestMessageTypes = null) + { + var knownTypes = new List(DefaultKnownTypes); + knownTypes.AddRange(wrappedRequestMessageTypes); + + DataContractSerializer serializer = this.CreateMessageBodyDataContractSerializer( + typeof(WrappedMessageBody), + knownTypes); + + return new MemoryStreamMessageBodySerializer(this, serializer); + } + + /// + /// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. + /// + /// The remoted service interface. + /// The return types of all of the methods of the specified interface. + /// Wrapped Response Types for all remoting methods. + /// + /// An instance of the that can serialize the service + /// actor response message body to a messaging body for transferring over the transport. + /// + public IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer( + Type serviceInterfaceType, + IEnumerable methodReturnTypes, + IEnumerable wrappedResponseMessageTypes = null) + { + var knownTypes = new List(DefaultKnownTypes); + knownTypes.AddRange(wrappedResponseMessageTypes); + + DataContractSerializer serializer = this.CreateMessageBodyDataContractSerializer( + typeof(WrappedMessageBody), + knownTypes); + + return new MemoryStreamMessageBodySerializer(this, serializer); + } + + /// + /// Create the writer to write to the stream. Use this method to customize how the serialized contents are written to + /// the stream. + /// + /// The stream on which to write the serialized contents. + /// + /// An using which the serializer will write the object on the + /// stream. + /// + internal XmlDictionaryWriter CreateXmlDictionaryWriter(Stream outputStream) + { + return XmlDictionaryWriter.CreateBinaryWriter(outputStream); + } + + /// + /// Create the reader to read from the input stream. Use this method to customize how the serialized contents are read + /// from the stream. + /// + /// The stream from which to read the serialized contents. + /// + /// An using which the serializer will read the object from the + /// stream. + /// + internal XmlDictionaryReader CreateXmlDictionaryReader(Stream inputStream) + { + return XmlDictionaryReader.CreateBinaryReader(inputStream, XmlDictionaryReaderQuotas.Max); + } + + /// + /// Gets the settings used to create DataContractSerializer for serializing and de-serializing request message body. + /// + /// Remoting RequestMessageBody Type. + /// The return types of all of the methods of the specified interface. + /// for serializing and de-serializing request message body. + internal DataContractSerializer CreateMessageBodyDataContractSerializer( + Type remotingRequestType, + IEnumerable knownTypes) + { + var serializer = new DataContractSerializer( + remotingRequestType, + new DataContractSerializerSettings() + { + MaxItemsInObjectGraph = int.MaxValue, + KnownTypes = knownTypes, + }); + + serializer.SetSerializationSurrogateProvider(new ActorDataContractSurrogate()); + return serializer; + } /// - /// This is the implmentation for used by remoting service and client during - /// request/response serialization . It uses request Wrapping and data contract for serialization. + /// Default serializer for service remoting request and response message body that uses the + /// memory stream to create outgoing message buffers. /// - internal class ActorMessageBodyDataContractSerializationProvider : IActorMessageBodySerializationProvider + private class MemoryStreamMessageBodySerializer : + IActorRequestMessageBodySerializer, + IActorResponseMessageBodySerializer + where TRequest : IActorRequestMessageBody + where TResponse : IActorResponseMessageBody { - private static readonly IEnumerable DefaultKnownTypes = new[] + private readonly ActorMessageBodyDataContractSerializationProvider serializationProvider; + private readonly DataContractSerializer serializer; + + public MemoryStreamMessageBodySerializer( + ActorMessageBodyDataContractSerializationProvider serializationProvider, + DataContractSerializer serializer) { - typeof(ActorReference), - }; + this.serializationProvider = serializationProvider; + this.serializer = serializer; + } - /// - /// Initializes a new instance of the class. - /// - public ActorMessageBodyDataContractSerializationProvider() + byte[] IActorRequestMessageBodySerializer.Serialize(IActorRequestMessageBody actorRequestMessageBody) { + if (actorRequestMessageBody == null) + { + return null; + } + + using var stream = new MemoryStream(); + using var writer = this.CreateXmlDictionaryWriter(stream); + this.serializer.WriteObject(writer, actorRequestMessageBody); + writer.Flush(); + + return stream.ToArray(); } - /// - /// Creates a MessageFactory for Wrapped Message DataContract Remoting Types. This is used to create Remoting Request/Response objects. - /// - /// - /// that provides an instance of the factory for creating - /// remoting request and response message bodies. - /// - public IActorMessageBodyFactory CreateMessageBodyFactory() + ValueTask IActorRequestMessageBodySerializer.DeserializeAsync(Stream stream) { - return new WrappedRequestMessageFactory(); + if (stream == null) + { + return default; + } + + if (stream.Length == 0) + { + return default; + } + + stream.Position = 0; + using var reader = this.CreateXmlDictionaryReader(stream); + return new ValueTask((TRequest)this.serializer.ReadObject(reader)); } - /// - /// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. - /// - /// The remoted service interface. - /// The union of parameter types of all of the methods of the specified interface. - /// Wrapped Request Types for all Methods. - /// - /// An instance of the that can serialize the service - /// actor request message body to a messaging body for transferring over the transport. - /// - public IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer( - Type serviceInterfaceType, - IEnumerable methodRequestParameterTypes, - IEnumerable wrappedRequestMessageTypes = null) + byte[] IActorResponseMessageBodySerializer.Serialize(IActorResponseMessageBody actorResponseMessageBody) { - var knownTypes = new List(DefaultKnownTypes); - knownTypes.AddRange(wrappedRequestMessageTypes); + if (actorResponseMessageBody == null) + { + return null; + } - DataContractSerializer serializer = this.CreateMessageBodyDataContractSerializer( - typeof(WrappedMessageBody), - knownTypes); + using var stream = new MemoryStream(); + using var writer = this.CreateXmlDictionaryWriter(stream); + this.serializer.WriteObject(writer, actorResponseMessageBody); + writer.Flush(); - return new MemoryStreamMessageBodySerializer(this, serializer); + return stream.ToArray(); } - /// - /// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. - /// - /// The remoted service interface. - /// The return types of all of the methods of the specified interface. - /// Wrapped Response Types for all remoting methods. - /// - /// An instance of the that can serialize the service - /// actor response message body to a messaging body for transferring over the transport. - /// - public IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer( - Type serviceInterfaceType, - IEnumerable methodReturnTypes, - IEnumerable wrappedResponseMessageTypes = null) + ValueTask IActorResponseMessageBodySerializer.DeserializeAsync(Stream messageBody) { - var knownTypes = new List(DefaultKnownTypes); - knownTypes.AddRange(wrappedResponseMessageTypes); + if (messageBody == null) + { + return default; + } - DataContractSerializer serializer = this.CreateMessageBodyDataContractSerializer( - typeof(WrappedMessageBody), - knownTypes); + // TODO check performance + using var stream = new MemoryStream(); + messageBody.CopyTo(stream); + stream.Position = 0; + + if (stream.Capacity == 0) + { + return default; + } - return new MemoryStreamMessageBodySerializer(this, serializer); + using var reader = this.CreateXmlDictionaryReader(stream); + return new ValueTask((TResponse)this.serializer.ReadObject(reader)); } /// @@ -109,9 +248,9 @@ public IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer( /// An using which the serializer will write the object on the /// stream. /// - internal XmlDictionaryWriter CreateXmlDictionaryWriter(Stream outputStream) + private XmlDictionaryWriter CreateXmlDictionaryWriter(Stream outputStream) { - return XmlDictionaryWriter.CreateBinaryWriter(outputStream); + return this.serializationProvider.CreateXmlDictionaryWriter(outputStream); } /// @@ -123,149 +262,9 @@ internal XmlDictionaryWriter CreateXmlDictionaryWriter(Stream outputStream) /// An using which the serializer will read the object from the /// stream. /// - internal XmlDictionaryReader CreateXmlDictionaryReader(Stream inputStream) - { - return XmlDictionaryReader.CreateBinaryReader(inputStream, XmlDictionaryReaderQuotas.Max); - } - - /// - /// Gets the settings used to create DataContractSerializer for serializing and de-serializing request message body. - /// - /// Remoting RequestMessageBody Type. - /// The return types of all of the methods of the specified interface. - /// for serializing and de-serializing request message body. - internal DataContractSerializer CreateMessageBodyDataContractSerializer( - Type remotingRequestType, - IEnumerable knownTypes) - { - var serializer = new DataContractSerializer( - remotingRequestType, - new DataContractSerializerSettings() - { - MaxItemsInObjectGraph = int.MaxValue, - KnownTypes = knownTypes, - }); - - serializer.SetSerializationSurrogateProvider(new ActorDataContractSurrogate()); - return serializer; - } - - /// - /// Default serializer for service remoting request and response message body that uses the - /// memory stream to create outgoing message buffers. - /// - private class MemoryStreamMessageBodySerializer : - IActorRequestMessageBodySerializer, - IActorResponseMessageBodySerializer - where TRequest : IActorRequestMessageBody - where TResponse : IActorResponseMessageBody + private XmlDictionaryReader CreateXmlDictionaryReader(Stream inputStream) { - private readonly ActorMessageBodyDataContractSerializationProvider serializationProvider; - private readonly DataContractSerializer serializer; - - public MemoryStreamMessageBodySerializer( - ActorMessageBodyDataContractSerializationProvider serializationProvider, - DataContractSerializer serializer) - { - this.serializationProvider = serializationProvider; - this.serializer = serializer; - } - - byte[] IActorRequestMessageBodySerializer.Serialize(IActorRequestMessageBody actorRequestMessageBody) - { - if (actorRequestMessageBody == null) - { - return null; - } - - using var stream = new MemoryStream(); - using var writer = this.CreateXmlDictionaryWriter(stream); - this.serializer.WriteObject(writer, actorRequestMessageBody); - writer.Flush(); - - return stream.ToArray(); - } - - ValueTask IActorRequestMessageBodySerializer.DeserializeAsync(Stream stream) - { - if (stream == null) - { - return default; - } - - if (stream.Length == 0) - { - return default; - } - - stream.Position = 0; - using var reader = this.CreateXmlDictionaryReader(stream); - return new ValueTask((TRequest)this.serializer.ReadObject(reader)); - } - - byte[] IActorResponseMessageBodySerializer.Serialize(IActorResponseMessageBody actorResponseMessageBody) - { - if (actorResponseMessageBody == null) - { - return null; - } - - using var stream = new MemoryStream(); - using var writer = this.CreateXmlDictionaryWriter(stream); - this.serializer.WriteObject(writer, actorResponseMessageBody); - writer.Flush(); - - return stream.ToArray(); - } - - ValueTask IActorResponseMessageBodySerializer.DeserializeAsync(Stream messageBody) - { - if (messageBody == null) - { - return default; - } - - // TODO check performance - using var stream = new MemoryStream(); - messageBody.CopyTo(stream); - stream.Position = 0; - - if (stream.Capacity == 0) - { - return default; - } - - using var reader = this.CreateXmlDictionaryReader(stream); - return new ValueTask((TResponse)this.serializer.ReadObject(reader)); - } - - /// - /// Create the writer to write to the stream. Use this method to customize how the serialized contents are written to - /// the stream. - /// - /// The stream on which to write the serialized contents. - /// - /// An using which the serializer will write the object on the - /// stream. - /// - private XmlDictionaryWriter CreateXmlDictionaryWriter(Stream outputStream) - { - return this.serializationProvider.CreateXmlDictionaryWriter(outputStream); - } - - /// - /// Create the reader to read from the input stream. Use this method to customize how the serialized contents are read - /// from the stream. - /// - /// The stream from which to read the serialized contents. - /// - /// An using which the serializer will read the object from the - /// stream. - /// - private XmlDictionaryReader CreateXmlDictionaryReader(Stream inputStream) - { - return this.serializationProvider.CreateXmlDictionaryReader(inputStream); - } + return this.serializationProvider.CreateXmlDictionaryReader(inputStream); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorMessageBodyJsonConverter.cs b/src/Dapr.Actors/Communication/ActorMessageBodyJsonConverter.cs index 0c77adb10..03fc13c89 100644 --- a/src/Dapr.Actors/Communication/ActorMessageBodyJsonConverter.cs +++ b/src/Dapr.Actors/Communication/ActorMessageBodyJsonConverter.cs @@ -16,80 +16,79 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +internal class ActorMessageBodyJsonConverter : JsonConverter { - internal class ActorMessageBodyJsonConverter : JsonConverter + private readonly List methodRequestParameterTypes; + private readonly List wrappedRequestMessageTypes; + private readonly Type wrapperMessageType; + + public ActorMessageBodyJsonConverter( + List methodRequestParameterTypes, + List wrappedRequestMessageTypes = null + ) { - private readonly List methodRequestParameterTypes; - private readonly List wrappedRequestMessageTypes; - private readonly Type wrapperMessageType; + this.methodRequestParameterTypes = methodRequestParameterTypes; + this.wrappedRequestMessageTypes = wrappedRequestMessageTypes; - public ActorMessageBodyJsonConverter( - List methodRequestParameterTypes, - List wrappedRequestMessageTypes = null - ) + if (this.wrappedRequestMessageTypes != null && this.wrappedRequestMessageTypes.Count == 1) { - this.methodRequestParameterTypes = methodRequestParameterTypes; - this.wrappedRequestMessageTypes = wrappedRequestMessageTypes; - - if (this.wrappedRequestMessageTypes != null && this.wrappedRequestMessageTypes.Count == 1) - { - this.wrapperMessageType = this.wrappedRequestMessageTypes[0]; - } + this.wrapperMessageType = this.wrappedRequestMessageTypes[0]; } + } - public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - // Ensure start-of-object, then advance - if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException(); - reader.Read(); + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Ensure start-of-object, then advance + if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException(); + reader.Read(); - // Ensure property name, then advance - if (reader.TokenType != JsonTokenType.PropertyName || reader.GetString() != "value") throw new JsonException(); + // Ensure property name, then advance + if (reader.TokenType != JsonTokenType.PropertyName || reader.GetString() != "value") throw new JsonException(); + reader.Read(); + + // If the value is null, return null. + if (reader.TokenType == JsonTokenType.Null) + { + // Read the end object token. reader.Read(); + return default; + } - // If the value is null, return null. - if (reader.TokenType == JsonTokenType.Null) - { - // Read the end object token. - reader.Read(); - return default; - } + // If the value is an object, deserialize it to wrapper message type + if (this.wrapperMessageType != null) + { + var value = JsonSerializer.Deserialize(ref reader, this.wrapperMessageType, options); - // If the value is an object, deserialize it to wrapper message type - if (this.wrapperMessageType != null) + // Construct a new WrappedMessageBody with the deserialized value. + var wrapper = new WrappedMessageBody() { - var value = JsonSerializer.Deserialize(ref reader, this.wrapperMessageType, options); + Value = value, + }; - // Construct a new WrappedMessageBody with the deserialized value. - var wrapper = new WrappedMessageBody() - { - Value = value, - }; + // Read the end object token. + reader.Read(); - // Read the end object token. - reader.Read(); + // Coerce the type to T; required because WrappedMessageBody inherits from two separate interfaces, which + // cannot both be used as generic constraints + return (T)(object)wrapper; + } - // Coerce the type to T; required because WrappedMessageBody inherits from two separate interfaces, which - // cannot both be used as generic constraints - return (T)(object)wrapper; - } + return JsonSerializer.Deserialize(ref reader, options); + } - return JsonSerializer.Deserialize(ref reader, options); - } + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("value"); - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + if (value is WrappedMessageBody body) { - writer.WriteStartObject(); - writer.WritePropertyName("value"); - - if (value is WrappedMessageBody body) - { - JsonSerializer.Serialize(writer, body.Value, body.Value.GetType(), options); - } - else - writer.WriteNullValue(); - writer.WriteEndObject(); + JsonSerializer.Serialize(writer, body.Value, body.Value.GetType(), options); } + else + writer.WriteNullValue(); + writer.WriteEndObject(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs b/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs index 88bc11cef..ce84624d6 100644 --- a/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs +++ b/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs @@ -11,167 +11,166 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; + +/// +/// This is the implmentation for used by remoting service and client during +/// request/response serialization . It uses request Wrapping and data contract for serialization. +/// +internal class ActorMessageBodyJsonSerializationProvider : IActorMessageBodySerializationProvider { - using System; - using System.Collections.Generic; - using System.IO; - using System.Text.Json; - using System.Threading.Tasks; + public JsonSerializerOptions Options { get; } /// - /// This is the implmentation for used by remoting service and client during - /// request/response serialization . It uses request Wrapping and data contract for serialization. + /// Initializes a new instance of the class. /// - internal class ActorMessageBodyJsonSerializationProvider : IActorMessageBodySerializationProvider + public ActorMessageBodyJsonSerializationProvider(JsonSerializerOptions options) { - public JsonSerializerOptions Options { get; } + Options = options; + } - /// - /// Initializes a new instance of the class. - /// - public ActorMessageBodyJsonSerializationProvider(JsonSerializerOptions options) - { - Options = options; - } + /// + /// Creates a MessageFactory for Wrapped Message Json Remoting Types. This is used to create Remoting Request/Response objects. + /// + /// + /// that provides an instance of the factory for creating + /// remoting request and response message bodies. + /// + public IActorMessageBodyFactory CreateMessageBodyFactory() + { + return new WrappedRequestMessageFactory(); + } - /// - /// Creates a MessageFactory for Wrapped Message Json Remoting Types. This is used to create Remoting Request/Response objects. - /// - /// - /// that provides an instance of the factory for creating - /// remoting request and response message bodies. - /// - public IActorMessageBodyFactory CreateMessageBodyFactory() - { - return new WrappedRequestMessageFactory(); - } + /// + /// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message Json implementation. + /// + /// The remoted service interface. + /// The union of parameter types of all of the methods of the specified interface. + /// Wrapped Request Types for all Methods. + /// + /// An instance of the that can serialize the service + /// actor request message body to a messaging body for transferring over the transport. + /// + public IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer( + Type serviceInterfaceType, + IEnumerable methodRequestParameterTypes, + IEnumerable wrappedRequestMessageTypes = null) + { + return new MemoryStreamMessageBodySerializer(Options, serviceInterfaceType, methodRequestParameterTypes, wrappedRequestMessageTypes); + } + + /// + /// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message Json implementation. + /// + /// The remoted service interface. + /// The return types of all of the methods of the specified interface. + /// Wrapped Response Types for all remoting methods. + /// + /// An instance of the that can serialize the service + /// actor response message body to a messaging body for transferring over the transport. + /// + public IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer( + Type serviceInterfaceType, + IEnumerable methodReturnTypes, + IEnumerable wrappedResponseMessageTypes = null) + { + return new MemoryStreamMessageBodySerializer(Options, serviceInterfaceType, methodReturnTypes, wrappedResponseMessageTypes); + } + + /// + /// Default serializer for service remoting request and response message body that uses the + /// memory stream to create outgoing message buffers. + /// + private class MemoryStreamMessageBodySerializer : + IActorRequestMessageBodySerializer, + IActorResponseMessageBodySerializer + where TRequest : IActorRequestMessageBody + where TResponse : IActorResponseMessageBody + { + private readonly JsonSerializerOptions serializerOptions; - /// - /// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message Json implementation. - /// - /// The remoted service interface. - /// The union of parameter types of all of the methods of the specified interface. - /// Wrapped Request Types for all Methods. - /// - /// An instance of the that can serialize the service - /// actor request message body to a messaging body for transferring over the transport. - /// - public IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer( + public MemoryStreamMessageBodySerializer( + JsonSerializerOptions serializerOptions, Type serviceInterfaceType, IEnumerable methodRequestParameterTypes, IEnumerable wrappedRequestMessageTypes = null) { - return new MemoryStreamMessageBodySerializer(Options, serviceInterfaceType, methodRequestParameterTypes, wrappedRequestMessageTypes); - } + var _methodRequestParameterTypes = new List(methodRequestParameterTypes); + var _wrappedRequestMessageTypes = new List(wrappedRequestMessageTypes); + if(_wrappedRequestMessageTypes.Count > 1){ + throw new NotSupportedException("JSON serialisation should always provide the actor method (or nothing), that was called" + + " to support (de)serialisation. This is a Dapr SDK error, open an issue on GitHub."); + } + this.serializerOptions = new(serializerOptions) + { + // Workaround since WrappedMessageBody creates an object + // with parameters as fields + IncludeFields = true, + }; - /// - /// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message Json implementation. - /// - /// The remoted service interface. - /// The return types of all of the methods of the specified interface. - /// Wrapped Response Types for all remoting methods. - /// - /// An instance of the that can serialize the service - /// actor response message body to a messaging body for transferring over the transport. - /// - public IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer( - Type serviceInterfaceType, - IEnumerable methodReturnTypes, - IEnumerable wrappedResponseMessageTypes = null) - { - return new MemoryStreamMessageBodySerializer(Options, serviceInterfaceType, methodReturnTypes, wrappedResponseMessageTypes); + this.serializerOptions.Converters.Add(new ActorMessageBodyJsonConverter(_methodRequestParameterTypes, _wrappedRequestMessageTypes)); + this.serializerOptions.Converters.Add(new ActorMessageBodyJsonConverter(_methodRequestParameterTypes, _wrappedRequestMessageTypes)); } - /// - /// Default serializer for service remoting request and response message body that uses the - /// memory stream to create outgoing message buffers. - /// - private class MemoryStreamMessageBodySerializer : - IActorRequestMessageBodySerializer, - IActorResponseMessageBodySerializer - where TRequest : IActorRequestMessageBody - where TResponse : IActorResponseMessageBody + byte[] IActorRequestMessageBodySerializer.Serialize(IActorRequestMessageBody actorRequestMessageBody) { - private readonly JsonSerializerOptions serializerOptions; - - public MemoryStreamMessageBodySerializer( - JsonSerializerOptions serializerOptions, - Type serviceInterfaceType, - IEnumerable methodRequestParameterTypes, - IEnumerable wrappedRequestMessageTypes = null) + if (actorRequestMessageBody == null) { - var _methodRequestParameterTypes = new List(methodRequestParameterTypes); - var _wrappedRequestMessageTypes = new List(wrappedRequestMessageTypes); - if(_wrappedRequestMessageTypes.Count > 1){ - throw new NotSupportedException("JSON serialisation should always provide the actor method (or nothing), that was called" + - " to support (de)serialisation. This is a Dapr SDK error, open an issue on GitHub."); - } - this.serializerOptions = new(serializerOptions) - { - // Workaround since WrappedMessageBody creates an object - // with parameters as fields - IncludeFields = true, - }; - - this.serializerOptions.Converters.Add(new ActorMessageBodyJsonConverter(_methodRequestParameterTypes, _wrappedRequestMessageTypes)); - this.serializerOptions.Converters.Add(new ActorMessageBodyJsonConverter(_methodRequestParameterTypes, _wrappedRequestMessageTypes)); + return null; } - byte[] IActorRequestMessageBodySerializer.Serialize(IActorRequestMessageBody actorRequestMessageBody) - { - if (actorRequestMessageBody == null) - { - return null; - } + return JsonSerializer.SerializeToUtf8Bytes(actorRequestMessageBody, this.serializerOptions); + } - return JsonSerializer.SerializeToUtf8Bytes(actorRequestMessageBody, this.serializerOptions); + async ValueTask IActorRequestMessageBodySerializer.DeserializeAsync(Stream stream) + { + if (stream == null) + { + return default; } - async ValueTask IActorRequestMessageBodySerializer.DeserializeAsync(Stream stream) + if (stream.Length == 0) { - if (stream == null) - { - return default; - } - - if (stream.Length == 0) - { - return default; - } - - stream.Position = 0; - return await JsonSerializer.DeserializeAsync(stream, this.serializerOptions); + return default; } - byte[] IActorResponseMessageBodySerializer.Serialize(IActorResponseMessageBody actorResponseMessageBody) - { - if (actorResponseMessageBody == null) - { - return null; - } + stream.Position = 0; + return await JsonSerializer.DeserializeAsync(stream, this.serializerOptions); + } - return JsonSerializer.SerializeToUtf8Bytes(actorResponseMessageBody, this.serializerOptions); + byte[] IActorResponseMessageBodySerializer.Serialize(IActorResponseMessageBody actorResponseMessageBody) + { + if (actorResponseMessageBody == null) + { + return null; } - async ValueTask IActorResponseMessageBodySerializer.DeserializeAsync(Stream messageBody) - { - if (messageBody == null) - { - return null; - } + return JsonSerializer.SerializeToUtf8Bytes(actorResponseMessageBody, this.serializerOptions); + } - using var stream = new MemoryStream(); - messageBody.CopyTo(stream); - stream.Position = 0; + async ValueTask IActorResponseMessageBodySerializer.DeserializeAsync(Stream messageBody) + { + if (messageBody == null) + { + return null; + } - if (stream.Capacity == 0) - { - return null; - } + using var stream = new MemoryStream(); + messageBody.CopyTo(stream); + stream.Position = 0; - return await JsonSerializer.DeserializeAsync(stream, this.serializerOptions); + if (stream.Capacity == 0) + { + return null; } + + return await JsonSerializer.DeserializeAsync(stream, this.serializerOptions); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorMessageHeaderSerializer.cs b/src/Dapr.Actors/Communication/ActorMessageHeaderSerializer.cs index 7d0b2b730..17ead6d90 100644 --- a/src/Dapr.Actors/Communication/ActorMessageHeaderSerializer.cs +++ b/src/Dapr.Actors/Communication/ActorMessageHeaderSerializer.cs @@ -11,97 +11,96 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System.IO; - using System.Runtime.Serialization; - using System.Xml; +namespace Dapr.Actors.Communication; - internal class ActorMessageHeaderSerializer : IActorMessageHeaderSerializer - { - private readonly DataContractSerializer requestHeaderSerializer; - private readonly DataContractSerializer responseHeaderSerializer; +using System.IO; +using System.Runtime.Serialization; +using System.Xml; - public ActorMessageHeaderSerializer() - : this( - new DataContractSerializer( - typeof(IActorRequestMessageHeader), - new DataContractSerializerSettings() - { - MaxItemsInObjectGraph = int.MaxValue, - KnownTypes = new[] - { - typeof(ActorRequestMessageHeader), - }, - })) - { - } +internal class ActorMessageHeaderSerializer : IActorMessageHeaderSerializer +{ + private readonly DataContractSerializer requestHeaderSerializer; + private readonly DataContractSerializer responseHeaderSerializer; - public ActorMessageHeaderSerializer( - DataContractSerializer headerRequestSerializer) - { - this.requestHeaderSerializer = headerRequestSerializer; - this.responseHeaderSerializer = new DataContractSerializer( - typeof(IActorResponseMessageHeader), + public ActorMessageHeaderSerializer() + : this( + new DataContractSerializer( + typeof(IActorRequestMessageHeader), new DataContractSerializerSettings() { MaxItemsInObjectGraph = int.MaxValue, - KnownTypes = new[] { typeof(ActorResponseMessageHeader) }, - }); - } + KnownTypes = new[] + { + typeof(ActorRequestMessageHeader), + }, + })) + { + } - public byte[] SerializeRequestHeader(IActorRequestMessageHeader serviceRemotingRequestMessageHeader) - { - if (serviceRemotingRequestMessageHeader == null) + public ActorMessageHeaderSerializer( + DataContractSerializer headerRequestSerializer) + { + this.requestHeaderSerializer = headerRequestSerializer; + this.responseHeaderSerializer = new DataContractSerializer( + typeof(IActorResponseMessageHeader), + new DataContractSerializerSettings() { - return null; - } + MaxItemsInObjectGraph = int.MaxValue, + KnownTypes = new[] { typeof(ActorResponseMessageHeader) }, + }); + } - using var stream = new MemoryStream(); - using var writer = XmlDictionaryWriter.CreateTextWriter(stream); - this.requestHeaderSerializer.WriteObject(writer, serviceRemotingRequestMessageHeader); - writer.Flush(); - return stream.ToArray(); + public byte[] SerializeRequestHeader(IActorRequestMessageHeader serviceRemotingRequestMessageHeader) + { + if (serviceRemotingRequestMessageHeader == null) + { + return null; } - public IActorRequestMessageHeader DeserializeRequestHeaders(Stream messageHeader) - { - if ((messageHeader == null) || (messageHeader.Length == 0)) - { - return null; - } + using var stream = new MemoryStream(); + using var writer = XmlDictionaryWriter.CreateTextWriter(stream); + this.requestHeaderSerializer.WriteObject(writer, serviceRemotingRequestMessageHeader); + writer.Flush(); + return stream.ToArray(); + } - using var reader = XmlDictionaryReader.CreateTextReader( - messageHeader, - XmlDictionaryReaderQuotas.Max); - return (IActorRequestMessageHeader)this.requestHeaderSerializer.ReadObject(reader); + public IActorRequestMessageHeader DeserializeRequestHeaders(Stream messageHeader) + { + if ((messageHeader == null) || (messageHeader.Length == 0)) + { + return null; } - public byte[] SerializeResponseHeader(IActorResponseMessageHeader serviceRemotingResponseMessageHeader) - { - if (serviceRemotingResponseMessageHeader == null || serviceRemotingResponseMessageHeader.CheckIfItsEmpty()) - { - return null; - } + using var reader = XmlDictionaryReader.CreateTextReader( + messageHeader, + XmlDictionaryReaderQuotas.Max); + return (IActorRequestMessageHeader)this.requestHeaderSerializer.ReadObject(reader); + } - using var stream = new MemoryStream(); - using var writer = XmlDictionaryWriter.CreateTextWriter(stream); - this.responseHeaderSerializer.WriteObject(writer, serviceRemotingResponseMessageHeader); - writer.Flush(); - return stream.ToArray(); + public byte[] SerializeResponseHeader(IActorResponseMessageHeader serviceRemotingResponseMessageHeader) + { + if (serviceRemotingResponseMessageHeader == null || serviceRemotingResponseMessageHeader.CheckIfItsEmpty()) + { + return null; } - public IActorResponseMessageHeader DeserializeResponseHeaders(Stream messageHeader) - { - if ((messageHeader == null) || (messageHeader.Length == 0)) - { - return null; - } + using var stream = new MemoryStream(); + using var writer = XmlDictionaryWriter.CreateTextWriter(stream); + this.responseHeaderSerializer.WriteObject(writer, serviceRemotingResponseMessageHeader); + writer.Flush(); + return stream.ToArray(); + } - using var reader = XmlDictionaryReader.CreateTextReader( - messageHeader, - XmlDictionaryReaderQuotas.Max); - return (IActorResponseMessageHeader)this.responseHeaderSerializer.ReadObject(reader); + public IActorResponseMessageHeader DeserializeResponseHeaders(Stream messageHeader) + { + if ((messageHeader == null) || (messageHeader.Length == 0)) + { + return null; } + + using var reader = XmlDictionaryReader.CreateTextReader( + messageHeader, + XmlDictionaryReaderQuotas.Max); + return (IActorResponseMessageHeader)this.responseHeaderSerializer.ReadObject(reader); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorMessageSerializersManager.cs b/src/Dapr.Actors/Communication/ActorMessageSerializersManager.cs index 3355aff1a..4955a599e 100644 --- a/src/Dapr.Actors/Communication/ActorMessageSerializersManager.cs +++ b/src/Dapr.Actors/Communication/ActorMessageSerializersManager.cs @@ -11,105 +11,104 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using Dapr.Actors.Builder; - - internal class ActorMessageSerializersManager - { - private readonly ConcurrentDictionary<(int, string), CacheEntry> cachedBodySerializers; - private readonly IActorMessageHeaderSerializer headerSerializer; - private readonly IActorMessageBodySerializationProvider serializationProvider; +namespace Dapr.Actors.Communication; - public ActorMessageSerializersManager( - IActorMessageBodySerializationProvider serializationProvider, - IActorMessageHeaderSerializer headerSerializer) - { - if (serializationProvider == null) - { - serializationProvider = new ActorMessageBodyDataContractSerializationProvider(); - } +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Dapr.Actors.Builder; - if (headerSerializer == null) - { - headerSerializer = new ActorMessageHeaderSerializer(); - } - - this.serializationProvider = serializationProvider; - this.cachedBodySerializers = new ConcurrentDictionary<(int, string), CacheEntry>(); - this.headerSerializer = headerSerializer; - } +internal class ActorMessageSerializersManager +{ + private readonly ConcurrentDictionary<(int, string), CacheEntry> cachedBodySerializers; + private readonly IActorMessageHeaderSerializer headerSerializer; + private readonly IActorMessageBodySerializationProvider serializationProvider; - public IActorMessageBodySerializationProvider GetSerializationProvider() + public ActorMessageSerializersManager( + IActorMessageBodySerializationProvider serializationProvider, + IActorMessageHeaderSerializer headerSerializer) + { + if (serializationProvider == null) { - return this.serializationProvider; + serializationProvider = new ActorMessageBodyDataContractSerializationProvider(); } - public IActorMessageHeaderSerializer GetHeaderSerializer() + if (headerSerializer == null) { - return this.headerSerializer; + headerSerializer = new ActorMessageHeaderSerializer(); } - public IActorRequestMessageBodySerializer GetRequestMessageBodySerializer(int interfaceId, [AllowNull] string methodName = null) - { - return this.cachedBodySerializers.GetOrAdd((interfaceId, methodName), this.CreateSerializers).RequestMessageBodySerializer; - } + this.serializationProvider = serializationProvider; + this.cachedBodySerializers = new ConcurrentDictionary<(int, string), CacheEntry>(); + this.headerSerializer = headerSerializer; + } + + public IActorMessageBodySerializationProvider GetSerializationProvider() + { + return this.serializationProvider; + } + + public IActorMessageHeaderSerializer GetHeaderSerializer() + { + return this.headerSerializer; + } - public IActorResponseMessageBodySerializer GetResponseMessageBodySerializer(int interfaceId, [AllowNull] string methodName = null) + public IActorRequestMessageBodySerializer GetRequestMessageBodySerializer(int interfaceId, [AllowNull] string methodName = null) + { + return this.cachedBodySerializers.GetOrAdd((interfaceId, methodName), this.CreateSerializers).RequestMessageBodySerializer; + } + + public IActorResponseMessageBodySerializer GetResponseMessageBodySerializer(int interfaceId, [AllowNull] string methodName = null) + { + return this.cachedBodySerializers.GetOrAdd((interfaceId, methodName), this.CreateSerializers).ResponseMessageBodySerializer; + } + + internal CacheEntry CreateSerializers((int interfaceId, string methodName) data) + { + var interfaceDetails = this.GetInterfaceDetails(data.interfaceId); + + // get the service interface type from the code gen layer + var serviceInterfaceType = interfaceDetails.ServiceInterfaceType; + + // get the known types from the codegen layer + var requestBodyTypes = interfaceDetails.RequestKnownTypes; + + // get the known types from the codegen layer + var responseBodyTypes = interfaceDetails.ResponseKnownTypes; + if (data.methodName is null) { - return this.cachedBodySerializers.GetOrAdd((interfaceId, methodName), this.CreateSerializers).ResponseMessageBodySerializer; + // Path is mainly used for XML serialization + return new CacheEntry( + this.serializationProvider.CreateRequestMessageBodySerializer(serviceInterfaceType, requestBodyTypes, interfaceDetails.RequestWrappedKnownTypes), + this.serializationProvider.CreateResponseMessageBodySerializer(serviceInterfaceType, responseBodyTypes, interfaceDetails.ResponseWrappedKnownTypes)); } - - internal CacheEntry CreateSerializers((int interfaceId, string methodName) data) + else { - var interfaceDetails = this.GetInterfaceDetails(data.interfaceId); - - // get the service interface type from the code gen layer - var serviceInterfaceType = interfaceDetails.ServiceInterfaceType; - - // get the known types from the codegen layer - var requestBodyTypes = interfaceDetails.RequestKnownTypes; - - // get the known types from the codegen layer - var responseBodyTypes = interfaceDetails.ResponseKnownTypes; - if (data.methodName is null) - { - // Path is mainly used for XML serialization - return new CacheEntry( - this.serializationProvider.CreateRequestMessageBodySerializer(serviceInterfaceType, requestBodyTypes, interfaceDetails.RequestWrappedKnownTypes), - this.serializationProvider.CreateResponseMessageBodySerializer(serviceInterfaceType, responseBodyTypes, interfaceDetails.ResponseWrappedKnownTypes)); + // This path should be used for JSON serialization + var requestWrapperTypeAsList = interfaceDetails.RequestWrappedKnownTypes.Where(r => r.Name == $"{data.methodName}ReqBody").ToList(); + if(requestWrapperTypeAsList.Count > 1){ + throw new NotSupportedException($"More then one wrappertype was found for {data.methodName}"); } - else - { - // This path should be used for JSON serialization - var requestWrapperTypeAsList = interfaceDetails.RequestWrappedKnownTypes.Where(r => r.Name == $"{data.methodName}ReqBody").ToList(); - if(requestWrapperTypeAsList.Count > 1){ - throw new NotSupportedException($"More then one wrappertype was found for {data.methodName}"); - } - var responseWrapperTypeAsList = interfaceDetails.ResponseWrappedKnownTypes.Where(r => r.Name == $"{data.methodName}RespBody").ToList(); - if(responseWrapperTypeAsList.Count > 1){ - throw new NotSupportedException($"More then one wrappertype was found for {data.methodName}"); - } - return new CacheEntry( - this.serializationProvider.CreateRequestMessageBodySerializer(serviceInterfaceType, requestBodyTypes, requestWrapperTypeAsList), - this.serializationProvider.CreateResponseMessageBodySerializer(serviceInterfaceType, responseBodyTypes, responseWrapperTypeAsList)); + var responseWrapperTypeAsList = interfaceDetails.ResponseWrappedKnownTypes.Where(r => r.Name == $"{data.methodName}RespBody").ToList(); + if(responseWrapperTypeAsList.Count > 1){ + throw new NotSupportedException($"More then one wrappertype was found for {data.methodName}"); } - + return new CacheEntry( + this.serializationProvider.CreateRequestMessageBodySerializer(serviceInterfaceType, requestBodyTypes, requestWrapperTypeAsList), + this.serializationProvider.CreateResponseMessageBodySerializer(serviceInterfaceType, responseBodyTypes, responseWrapperTypeAsList)); } - internal InterfaceDetails GetInterfaceDetails(int interfaceId) - { - if (!ActorCodeBuilder.TryGetKnownTypes(interfaceId, out var interfaceDetails)) - { - throw new ArgumentException("No interface found with this Id " + interfaceId); - } + } - return interfaceDetails; + internal InterfaceDetails GetInterfaceDetails(int interfaceId) + { + if (!ActorCodeBuilder.TryGetKnownTypes(interfaceId, out var interfaceDetails)) + { + throw new ArgumentException("No interface found with this Id " + interfaceId); } + + return interfaceDetails; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorMethodDispatcherMap.cs b/src/Dapr.Actors/Communication/ActorMethodDispatcherMap.cs index 8f15a271d..a9000a8ae 100644 --- a/src/Dapr.Actors/Communication/ActorMethodDispatcherMap.cs +++ b/src/Dapr.Actors/Communication/ActorMethodDispatcherMap.cs @@ -11,43 +11,42 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System; +using System.Collections.Generic; +using System.Globalization; +using Dapr.Actors.Builder; +using Dapr.Actors.Resources; + +/// +/// Actor method dispatcher map for remoting calls. +/// +internal class ActorMethodDispatcherMap { - using System; - using System.Collections.Generic; - using System.Globalization; - using Dapr.Actors.Builder; - using Dapr.Actors.Resources; + private readonly IDictionary map; - /// - /// Actor method dispatcher map for remoting calls. - /// - internal class ActorMethodDispatcherMap + public ActorMethodDispatcherMap(IEnumerable interfaceTypes) { - private readonly IDictionary map; + this.map = new Dictionary(); - public ActorMethodDispatcherMap(IEnumerable interfaceTypes) + foreach (var actorInterfaceType in interfaceTypes) { - this.map = new Dictionary(); - - foreach (var actorInterfaceType in interfaceTypes) - { - var methodDispatcher = ActorCodeBuilder.GetOrCreateMethodDispatcher(actorInterfaceType); - this.map.Add(methodDispatcher.InterfaceId, methodDispatcher); - } + var methodDispatcher = ActorCodeBuilder.GetOrCreateMethodDispatcher(actorInterfaceType); + this.map.Add(methodDispatcher.InterfaceId, methodDispatcher); } + } - public ActorMethodDispatcherBase GetDispatcher(int interfaceId) + public ActorMethodDispatcherBase GetDispatcher(int interfaceId) + { + if (!this.map.TryGetValue(interfaceId, out var methodDispatcher)) { - if (!this.map.TryGetValue(interfaceId, out var methodDispatcher)) - { - throw new KeyNotFoundException(string.Format( - CultureInfo.CurrentCulture, - SR.ErrorMethodDispatcherNotFound, - interfaceId)); - } - - return methodDispatcher; + throw new KeyNotFoundException(string.Format( + CultureInfo.CurrentCulture, + SR.ErrorMethodDispatcherNotFound, + interfaceId)); } + + return methodDispatcher; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorRequestMessage.cs b/src/Dapr.Actors/Communication/ActorRequestMessage.cs index e123f0730..94917c59a 100644 --- a/src/Dapr.Actors/Communication/ActorRequestMessage.cs +++ b/src/Dapr.Actors/Communication/ActorRequestMessage.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +internal class ActorRequestMessage : IActorRequestMessage { - internal class ActorRequestMessage : IActorRequestMessage - { - private readonly IActorRequestMessageHeader header; - private readonly IActorRequestMessageBody msgBody; + private readonly IActorRequestMessageHeader header; + private readonly IActorRequestMessageBody msgBody; - public ActorRequestMessage(IActorRequestMessageHeader header, IActorRequestMessageBody msgBody) - { - this.header = header; - this.msgBody = msgBody; - } + public ActorRequestMessage(IActorRequestMessageHeader header, IActorRequestMessageBody msgBody) + { + this.header = header; + this.msgBody = msgBody; + } - public IActorRequestMessageHeader GetHeader() - { - return this.header; - } + public IActorRequestMessageHeader GetHeader() + { + return this.header; + } - public IActorRequestMessageBody GetBody() - { - return this.msgBody; - } + public IActorRequestMessageBody GetBody() + { + return this.msgBody; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorRequestMessageBody.cs b/src/Dapr.Actors/Communication/ActorRequestMessageBody.cs index 9355abf74..4d629eef2 100644 --- a/src/Dapr.Actors/Communication/ActorRequestMessageBody.cs +++ b/src/Dapr.Actors/Communication/ActorRequestMessageBody.cs @@ -11,31 +11,30 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +[DataContract(Name = "msgBody", Namespace = Constants.Namespace)] +internal class ActorRequestMessageBody : IActorRequestMessageBody { - using System; - using System.Collections.Generic; - using System.Runtime.Serialization; + [DataMember] + private readonly Dictionary parameters; - [DataContract(Name = "msgBody", Namespace = Constants.Namespace)] - internal class ActorRequestMessageBody : IActorRequestMessageBody + public ActorRequestMessageBody(int parameterInfos) { - [DataMember] - private readonly Dictionary parameters; - - public ActorRequestMessageBody(int parameterInfos) - { - this.parameters = new Dictionary(parameterInfos); - } + this.parameters = new Dictionary(parameterInfos); + } - public void SetParameter(int position, string paramName, object parameter) - { - this.parameters[paramName] = parameter; - } + public void SetParameter(int position, string paramName, object parameter) + { + this.parameters[paramName] = parameter; + } - public object GetParameter(int position, string paramName, Type paramType) - { - return this.parameters[paramName]; - } + public object GetParameter(int position, string paramName, Type paramType) + { + return this.parameters[paramName]; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorRequestMessageHeader.cs b/src/Dapr.Actors/Communication/ActorRequestMessageHeader.cs index 3141220c5..99b9a3a45 100644 --- a/src/Dapr.Actors/Communication/ActorRequestMessageHeader.cs +++ b/src/Dapr.Actors/Communication/ActorRequestMessageHeader.cs @@ -11,94 +11,93 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.Serialization; +using Dapr.Actors.Resources; + +[DataContract(Name = "ActorHeader", Namespace = Constants.Namespace)] +internal class ActorRequestMessageHeader : IActorRequestMessageHeader { - using System.Collections.Generic; - using System.Globalization; - using System.Runtime.Serialization; - using Dapr.Actors.Resources; + internal const string CancellationHeaderName = "CancellationHeader"; - [DataContract(Name = "ActorHeader", Namespace = Constants.Namespace)] - internal class ActorRequestMessageHeader : IActorRequestMessageHeader + /// + /// Initializes a new instance of the class. + /// + public ActorRequestMessageHeader() { - internal const string CancellationHeaderName = "CancellationHeader"; - - /// - /// Initializes a new instance of the class. - /// - public ActorRequestMessageHeader() - { - this.headers = new Dictionary(); - this.InvocationId = null; - } + this.headers = new Dictionary(); + this.InvocationId = null; + } - /// - /// Gets or sets the methodId of the remote method. - /// - /// Method id. - [DataMember(Name = "MethodId", IsRequired = true, Order = 0)] - public int MethodId { get; set; } + /// + /// Gets or sets the methodId of the remote method. + /// + /// Method id. + [DataMember(Name = "MethodId", IsRequired = true, Order = 0)] + public int MethodId { get; set; } - /// - /// Gets or sets the interface id of the remote interface. - /// - /// Interface id. - [DataMember(Name = "InterfaceId", IsRequired = true, Order = 1)] - public int InterfaceId { get; set; } + /// + /// Gets or sets the interface id of the remote interface. + /// + /// Interface id. + [DataMember(Name = "InterfaceId", IsRequired = true, Order = 1)] + public int InterfaceId { get; set; } - /// - /// Gets or sets identifier for the remote method invocation. - /// - [DataMember(Name = "InvocationId", IsRequired = false, Order = 2, EmitDefaultValue = false)] - public string InvocationId { get; set; } + /// + /// Gets or sets identifier for the remote method invocation. + /// + [DataMember(Name = "InvocationId", IsRequired = false, Order = 2, EmitDefaultValue = false)] + public string InvocationId { get; set; } - [DataMember(IsRequired = false, Order = 3)] - public ActorId ActorId { get; set; } + [DataMember(IsRequired = false, Order = 3)] + public ActorId ActorId { get; set; } - [DataMember(IsRequired = false, Order = 4)] - public string CallContext { get; set; } + [DataMember(IsRequired = false, Order = 4)] + public string CallContext { get; set; } - [DataMember(Name = "Headers", IsRequired = true, Order = 5)] + [DataMember(Name = "Headers", IsRequired = true, Order = 5)] #pragma warning disable SA1201 // Elements should appear in the correct order. Increases readbility when fields kept in order. - private readonly Dictionary headers; + private readonly Dictionary headers; #pragma warning restore SA1201 // Elements should appear in the correct order - /// - /// Gets or sets the method name of the remote method. - /// - /// Method Name. - [DataMember(Name = "MethodName", IsRequired = false, Order = 6)] - public string MethodName { get; set; } + /// + /// Gets or sets the method name of the remote method. + /// + /// Method Name. + [DataMember(Name = "MethodName", IsRequired = false, Order = 6)] + public string MethodName { get; set; } - [DataMember(IsRequired = false, Order = 7)] - public string ActorType { get; set; } + [DataMember(IsRequired = false, Order = 7)] + public string ActorType { get; set; } - public void AddHeader(string headerName, byte[] headerValue) + public void AddHeader(string headerName, byte[] headerValue) + { + if (this.headers.ContainsKey(headerName)) { - if (this.headers.ContainsKey(headerName)) - { - // TODO throw specific translated exception type - throw new System.Exception( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorHeaderAlreadyExists, - headerName)); - } - - this.headers[headerName] = headerValue; + // TODO throw specific translated exception type + throw new System.Exception( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorHeaderAlreadyExists, + headerName)); } - public bool TryGetHeaderValue(string headerName, out byte[] headerValue) - { - headerValue = null; + this.headers[headerName] = headerValue; + } - if (this.headers == null) - { - return false; - } + public bool TryGetHeaderValue(string headerName, out byte[] headerValue) + { + headerValue = null; - return this.headers.TryGetValue(headerName, out headerValue); + if (this.headers == null) + { + return false; } + + return this.headers.TryGetValue(headerName, out headerValue); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorResponseMessage.cs b/src/Dapr.Actors/Communication/ActorResponseMessage.cs index dcebe8309..d9d35edc6 100644 --- a/src/Dapr.Actors/Communication/ActorResponseMessage.cs +++ b/src/Dapr.Actors/Communication/ActorResponseMessage.cs @@ -11,29 +11,28 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +internal class ActorResponseMessage : IActorResponseMessage { - internal class ActorResponseMessage : IActorResponseMessage - { - private readonly IActorResponseMessageHeader header; - private readonly IActorResponseMessageBody msgBody; + private readonly IActorResponseMessageHeader header; + private readonly IActorResponseMessageBody msgBody; - public ActorResponseMessage( - IActorResponseMessageHeader header, - IActorResponseMessageBody msgBody) - { - this.header = header; - this.msgBody = msgBody; - } + public ActorResponseMessage( + IActorResponseMessageHeader header, + IActorResponseMessageBody msgBody) + { + this.header = header; + this.msgBody = msgBody; + } - public IActorResponseMessageHeader GetHeader() - { - return this.header; - } + public IActorResponseMessageHeader GetHeader() + { + return this.header; + } - public IActorResponseMessageBody GetBody() - { - return this.msgBody; - } + public IActorResponseMessageBody GetBody() + { + return this.msgBody; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorResponseMessageBody.cs b/src/Dapr.Actors/Communication/ActorResponseMessageBody.cs index 8ed4457ad..c5ef09477 100644 --- a/src/Dapr.Actors/Communication/ActorResponseMessageBody.cs +++ b/src/Dapr.Actors/Communication/ActorResponseMessageBody.cs @@ -11,25 +11,24 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System; +using System.Runtime.Serialization; + +[DataContract(Name = "msgResponse", Namespace = Constants.Namespace)] +internal class ActorResponseMessageBody : IActorResponseMessageBody { - using System; - using System.Runtime.Serialization; + [DataMember] + private object response; - [DataContract(Name = "msgResponse", Namespace = Constants.Namespace)] - internal class ActorResponseMessageBody : IActorResponseMessageBody + public void Set(object response) { - [DataMember] - private object response; - - public void Set(object response) - { - this.response = response; - } + this.response = response; + } - public object Get(Type paramType) - { - return this.response; - } + public object Get(Type paramType) + { + return this.response; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorResponseMessageHeader.cs b/src/Dapr.Actors/Communication/ActorResponseMessageHeader.cs index 00f23fcea..ef67c4f2e 100644 --- a/src/Dapr.Actors/Communication/ActorResponseMessageHeader.cs +++ b/src/Dapr.Actors/Communication/ActorResponseMessageHeader.cs @@ -11,63 +11,62 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System.Collections.Generic; - using System.Globalization; - using System.Runtime.Serialization; - using Dapr.Actors.Resources; +namespace Dapr.Actors.Communication; + +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.Serialization; +using Dapr.Actors.Resources; - [DataContract(Name = "ActorResponseMessageHeaders", Namespace = Constants.Namespace)] +[DataContract(Name = "ActorResponseMessageHeaders", Namespace = Constants.Namespace)] - internal class ActorResponseMessageHeader : IActorResponseMessageHeader +internal class ActorResponseMessageHeader : IActorResponseMessageHeader +{ + [DataMember(Name = "Headers", IsRequired = true, Order = 2)] + private readonly Dictionary headers; + + /// + /// Initializes a new instance of the class. + /// + public ActorResponseMessageHeader() { - [DataMember(Name = "Headers", IsRequired = true, Order = 2)] - private readonly Dictionary headers; + this.headers = new Dictionary(); + } - /// - /// Initializes a new instance of the class. - /// - public ActorResponseMessageHeader() + public void AddHeader(string headerName, byte[] headerValue) + { + if (this.headers.ContainsKey(headerName)) { - this.headers = new Dictionary(); + // TODO throw Dapr specific translated exception type + throw new System.Exception( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorHeaderAlreadyExists, + headerName)); } - public void AddHeader(string headerName, byte[] headerValue) - { - if (this.headers.ContainsKey(headerName)) - { - // TODO throw Dapr specific translated exception type - throw new System.Exception( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorHeaderAlreadyExists, - headerName)); - } - - this.headers[headerName] = headerValue; - } + this.headers[headerName] = headerValue; + } - public bool CheckIfItsEmpty() + public bool CheckIfItsEmpty() + { + if (this.headers == null || this.headers.Count == 0) { - if (this.headers == null || this.headers.Count == 0) - { - return true; - } - - return false; + return true; } - public bool TryGetHeaderValue(string headerName, out byte[] headerValue) - { - headerValue = null; + return false; + } - if (this.headers == null) - { - return false; - } + public bool TryGetHeaderValue(string headerName, out byte[] headerValue) + { + headerValue = null; - return this.headers.TryGetValue(headerName, out headerValue); + if (this.headers == null) + { + return false; } + + return this.headers.TryGetValue(headerName, out headerValue); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/ActorStateResponse.cs b/src/Dapr.Actors/Communication/ActorStateResponse.cs index 22b3bf20e..bf48467e7 100644 --- a/src/Dapr.Actors/Communication/ActorStateResponse.cs +++ b/src/Dapr.Actors/Communication/ActorStateResponse.cs @@ -11,40 +11,39 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System; +namespace Dapr.Actors.Communication; + +using System; +/// +/// Represents a response from fetching an actor state key. +/// +public class ActorStateResponse +{ /// - /// Represents a response from fetching an actor state key. + /// Initializes a new instance of the class. /// - public class ActorStateResponse + /// The response value. + /// The time to live expiration time. + public ActorStateResponse(T value, DateTimeOffset? ttlExpireTime) { - /// - /// Initializes a new instance of the class. - /// - /// The response value. - /// The time to live expiration time. - public ActorStateResponse(T value, DateTimeOffset? ttlExpireTime) - { - this.Value = value; - this.TTLExpireTime = ttlExpireTime; - } + this.Value = value; + this.TTLExpireTime = ttlExpireTime; + } - /// - /// Gets the response value as a string. - /// - /// - /// The response value as a string. - /// - public T Value { get; } + /// + /// Gets the response value as a string. + /// + /// + /// The response value as a string. + /// + public T Value { get; } - /// - /// Gets the time to live expiration time. - /// - /// - /// The time to live expiration time. - /// - public DateTimeOffset? TTLExpireTime { get; } - } -} + /// + /// Gets the time to live expiration time. + /// + /// + /// The time to live expiration time. + /// + public DateTimeOffset? TTLExpireTime { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/CacheEntry.cs b/src/Dapr.Actors/Communication/CacheEntry.cs index 41664087d..36a3a4da8 100644 --- a/src/Dapr.Actors/Communication/CacheEntry.cs +++ b/src/Dapr.Actors/Communication/CacheEntry.cs @@ -11,20 +11,19 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +internal class CacheEntry { - internal class CacheEntry + public CacheEntry( + IActorRequestMessageBodySerializer requestBodySerializer, + IActorResponseMessageBodySerializer responseBodySerializer) { - public CacheEntry( - IActorRequestMessageBodySerializer requestBodySerializer, - IActorResponseMessageBodySerializer responseBodySerializer) - { - this.RequestMessageBodySerializer = requestBodySerializer; - this.ResponseMessageBodySerializer = responseBodySerializer; - } + this.RequestMessageBodySerializer = requestBodySerializer; + this.ResponseMessageBodySerializer = responseBodySerializer; + } - public IActorRequestMessageBodySerializer RequestMessageBodySerializer { get; } + public IActorRequestMessageBodySerializer RequestMessageBodySerializer { get; } - public IActorResponseMessageBodySerializer ResponseMessageBodySerializer { get; } - } -} + public IActorResponseMessageBodySerializer ResponseMessageBodySerializer { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/Client/ActorNonRemotingClient.cs b/src/Dapr.Actors/Communication/Client/ActorNonRemotingClient.cs index d9e7f8403..83989cea0 100644 --- a/src/Dapr.Actors/Communication/Client/ActorNonRemotingClient.cs +++ b/src/Dapr.Actors/Communication/Client/ActorNonRemotingClient.cs @@ -11,33 +11,32 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication.Client +namespace Dapr.Actors.Communication.Client; + +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +internal class ActorNonRemotingClient { - using System.IO; - using System.Threading; - using System.Threading.Tasks; + private readonly IDaprInteractor daprInteractor; - internal class ActorNonRemotingClient + public ActorNonRemotingClient(IDaprInteractor daprInteractor) { - private readonly IDaprInteractor daprInteractor; - - public ActorNonRemotingClient(IDaprInteractor daprInteractor) - { - this.daprInteractor = daprInteractor; - } + this.daprInteractor = daprInteractor; + } - /// - /// Invokes an Actor method on Dapr runtime without remoting. - /// - /// Type of actor. - /// ActorId. - /// Method name to invoke. - /// Serialized body. - /// Cancels the operation. - /// A task that represents the asynchronous operation. - public Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default) - { - return this.daprInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, jsonPayload, cancellationToken); - } + /// + /// Invokes an Actor method on Dapr runtime without remoting. + /// + /// Type of actor. + /// ActorId. + /// Method name to invoke. + /// Serialized body. + /// Cancels the operation. + /// A task that represents the asynchronous operation. + public Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default) + { + return this.daprInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, jsonPayload, cancellationToken); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/Client/ActorRemotingClient.cs b/src/Dapr.Actors/Communication/Client/ActorRemotingClient.cs index d43accc60..9a019e804 100644 --- a/src/Dapr.Actors/Communication/Client/ActorRemotingClient.cs +++ b/src/Dapr.Actors/Communication/Client/ActorRemotingClient.cs @@ -11,49 +11,48 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication.Client +namespace Dapr.Actors.Communication.Client; + +using System.Threading; +using System.Threading.Tasks; + +internal class ActorRemotingClient { - using System.Threading; - using System.Threading.Tasks; + private readonly ActorMessageSerializersManager serializersManager; + private readonly IActorMessageBodyFactory remotingMessageBodyFactory = null; + private readonly IDaprInteractor daprInteractor; - internal class ActorRemotingClient + public ActorRemotingClient( + IDaprInteractor daprInteractor, + IActorMessageBodySerializationProvider serializationProvider = null) { - private readonly ActorMessageSerializersManager serializersManager; - private readonly IActorMessageBodyFactory remotingMessageBodyFactory = null; - private readonly IDaprInteractor daprInteractor; - - public ActorRemotingClient( - IDaprInteractor daprInteractor, - IActorMessageBodySerializationProvider serializationProvider = null) - { - this.daprInteractor = daprInteractor; - this.serializersManager = IntializeSerializationManager(serializationProvider); - this.remotingMessageBodyFactory = this.serializersManager.GetSerializationProvider().CreateMessageBodyFactory(); - } + this.daprInteractor = daprInteractor; + this.serializersManager = IntializeSerializationManager(serializationProvider); + this.remotingMessageBodyFactory = this.serializersManager.GetSerializationProvider().CreateMessageBodyFactory(); + } - /// - /// Gets a factory for creating the remoting message bodies. - /// - /// A factory for creating the remoting message bodies. - public IActorMessageBodyFactory GetRemotingMessageBodyFactory() - { - return this.remotingMessageBodyFactory; - } + /// + /// Gets a factory for creating the remoting message bodies. + /// + /// A factory for creating the remoting message bodies. + public IActorMessageBodyFactory GetRemotingMessageBodyFactory() + { + return this.remotingMessageBodyFactory; + } - public async Task InvokeAsync( - IActorRequestMessage remotingRequestMessage, - CancellationToken cancellationToken) - { - return await this.daprInteractor.InvokeActorMethodWithRemotingAsync(this.serializersManager, remotingRequestMessage, cancellationToken); - } + public async Task InvokeAsync( + IActorRequestMessage remotingRequestMessage, + CancellationToken cancellationToken) + { + return await this.daprInteractor.InvokeActorMethodWithRemotingAsync(this.serializersManager, remotingRequestMessage, cancellationToken); + } - private static ActorMessageSerializersManager IntializeSerializationManager( - IActorMessageBodySerializationProvider serializationProvider) - { - // TODO serializer settings - return new ActorMessageSerializersManager( - serializationProvider, - new ActorMessageHeaderSerializer()); - } + private static ActorMessageSerializersManager IntializeSerializationManager( + IActorMessageBodySerializationProvider serializationProvider) + { + // TODO serializer settings + return new ActorMessageSerializersManager( + serializationProvider, + new ActorMessageHeaderSerializer()); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/DataContractMessageFactory.cs b/src/Dapr.Actors/Communication/DataContractMessageFactory.cs index dd5cebef3..a58984b6d 100644 --- a/src/Dapr.Actors/Communication/DataContractMessageFactory.cs +++ b/src/Dapr.Actors/Communication/DataContractMessageFactory.cs @@ -11,18 +11,17 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +internal class DataContractMessageFactory : IActorMessageBodyFactory { - internal class DataContractMessageFactory : IActorMessageBodyFactory + public IActorRequestMessageBody CreateRequestMessageBody(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject) { - public IActorRequestMessageBody CreateRequestMessageBody(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject) - { - return new ActorRequestMessageBody(numberOfParameters); - } + return new ActorRequestMessageBody(numberOfParameters); + } - public IActorResponseMessageBody CreateResponseMessageBody(string interfaceName, string methodName, object wrappedResponseObject) - { - return new ActorResponseMessageBody(); - } + public IActorResponseMessageBody CreateResponseMessageBody(string interfaceName, string methodName, object wrappedResponseObject) + { + return new ActorResponseMessageBody(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/DisposableStream.cs b/src/Dapr.Actors/Communication/DisposableStream.cs index 311f2da33..c6b9abbf2 100644 --- a/src/Dapr.Actors/Communication/DisposableStream.cs +++ b/src/Dapr.Actors/Communication/DisposableStream.cs @@ -11,86 +11,85 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +using System.IO; + +/// +/// Wraps an implementation of Stream and provides an idempotent impplementation of Dispose. +/// +internal class DisposableStream : Stream { - using System.IO; + private readonly Stream streamImplementation; + private bool disposed; - /// - /// Wraps an implementation of Stream and provides an idempotent impplementation of Dispose. - /// - internal class DisposableStream : Stream + public DisposableStream(Stream streamImplementation) { - private readonly Stream streamImplementation; - private bool disposed; - - public DisposableStream(Stream streamImplementation) - { - this.streamImplementation = streamImplementation; - } + this.streamImplementation = streamImplementation; + } - public override bool CanRead - { - get { return this.streamImplementation.CanRead; } - } + public override bool CanRead + { + get { return this.streamImplementation.CanRead; } + } - public override bool CanSeek - { - get { return this.streamImplementation.CanSeek; } - } + public override bool CanSeek + { + get { return this.streamImplementation.CanSeek; } + } - public override bool CanWrite - { - get { return this.streamImplementation.CanWrite; } - } + public override bool CanWrite + { + get { return this.streamImplementation.CanWrite; } + } - public override long Length - { - get { return this.streamImplementation.Length; } - } + public override long Length + { + get { return this.streamImplementation.Length; } + } - public override long Position - { - get { return this.streamImplementation.Position; } - set { this.streamImplementation.Position = value; } - } + public override long Position + { + get { return this.streamImplementation.Position; } + set { this.streamImplementation.Position = value; } + } - public override void Flush() - { - this.streamImplementation.Flush(); - } + public override void Flush() + { + this.streamImplementation.Flush(); + } - public override long Seek(long offset, SeekOrigin origin) - { - return this.streamImplementation.Seek(offset, origin); - } + public override long Seek(long offset, SeekOrigin origin) + { + return this.streamImplementation.Seek(offset, origin); + } - public override void SetLength(long value) - { - this.streamImplementation.SetLength(value); - } + public override void SetLength(long value) + { + this.streamImplementation.SetLength(value); + } - public override int Read(byte[] buffer, int offset, int count) - { - return this.streamImplementation.Read(buffer, offset, count); - } + public override int Read(byte[] buffer, int offset, int count) + { + return this.streamImplementation.Read(buffer, offset, count); + } - public override void Write(byte[] buffer, int offset, int count) - { - this.streamImplementation.Write(buffer, offset, count); - } + public override void Write(byte[] buffer, int offset, int count) + { + this.streamImplementation.Write(buffer, offset, count); + } - protected override void Dispose(bool disposing) + protected override void Dispose(bool disposing) + { + if (!this.disposed) { - if (!this.disposed) + base.Dispose(disposing); + if (disposing) { - base.Dispose(disposing); - if (disposing) - { - this.streamImplementation.Dispose(); - } + this.streamImplementation.Dispose(); } - - this.disposed = true; } + + this.disposed = true; } } \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorMessageBodyFactory.cs b/src/Dapr.Actors/Communication/IActorMessageBodyFactory.cs index 2d7b95e6f..729ce5302 100644 --- a/src/Dapr.Actors/Communication/IActorMessageBodyFactory.cs +++ b/src/Dapr.Actors/Communication/IActorMessageBodyFactory.cs @@ -11,30 +11,29 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +/// +/// Defines the interface that must be implemented for providing factory for creating actor request body and response body objects. +/// +public interface IActorMessageBodyFactory { /// - /// Defines the interface that must be implemented for providing factory for creating actor request body and response body objects. + /// Creates a actor request message body. /// - public interface IActorMessageBodyFactory - { - /// - /// Creates a actor request message body. - /// - /// This is FullName for the service interface for which request body is being constructed. - /// MethodName for the service interface for which request will be sent to. - /// Number of Parameters in that Method. - /// Wrapped Request Object. - /// IActorRequestMessageBody. - IActorRequestMessageBody CreateRequestMessageBody(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject); + /// This is FullName for the service interface for which request body is being constructed. + /// MethodName for the service interface for which request will be sent to. + /// Number of Parameters in that Method. + /// Wrapped Request Object. + /// IActorRequestMessageBody. + IActorRequestMessageBody CreateRequestMessageBody(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject); - /// - /// Creates a actor response message body. - /// - /// This is FullName for the service interface for which request body is being constructed. - /// MethodName for the service interface for which request will be sent to. - /// Wrapped Response Object. - /// IActorResponseMessageBody. - IActorResponseMessageBody CreateResponseMessageBody(string interfaceName, string methodName, object wrappedResponseObject); - } -} + /// + /// Creates a actor response message body. + /// + /// This is FullName for the service interface for which request body is being constructed. + /// MethodName for the service interface for which request will be sent to. + /// Wrapped Response Object. + /// IActorResponseMessageBody. + IActorResponseMessageBody CreateResponseMessageBody(string interfaceName, string methodName, object wrappedResponseObject); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorMessageBodySerializationProvider.cs b/src/Dapr.Actors/Communication/IActorMessageBodySerializationProvider.cs index f42624a85..2380b3357 100644 --- a/src/Dapr.Actors/Communication/IActorMessageBodySerializationProvider.cs +++ b/src/Dapr.Actors/Communication/IActorMessageBodySerializationProvider.cs @@ -11,50 +11,49 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System; - using System.Collections.Generic; +namespace Dapr.Actors.Communication; + +using System; +using System.Collections.Generic; +/// +/// Defines the interface that must be implemented for providing custom serialization for the remoting request. +/// +internal interface IActorMessageBodySerializationProvider +{ /// - /// Defines the interface that must be implemented for providing custom serialization for the remoting request. + /// Create a IServiceRemotingMessageBodyFactory used for creating remoting request and response body. /// - internal interface IActorMessageBodySerializationProvider - { - /// - /// Create a IServiceRemotingMessageBodyFactory used for creating remoting request and response body. - /// - /// A custom that can be used for creating remoting request and response message bodies. - IActorMessageBodyFactory CreateMessageBodyFactory(); + /// A custom that can be used for creating remoting request and response message bodies. + IActorMessageBodyFactory CreateMessageBodyFactory(); - /// - /// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. - /// - /// The remoted service interface. - /// The union of parameter types of all of the methods of the specified interface. - /// Wrapped Request Types for all Methods. - /// - /// An instance of the that can serialize the service - /// actor request message body to a messaging body for transferring over the transport. - /// - IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer( - Type serviceInterfaceType, - IEnumerable methodRequestParameterTypes, - IEnumerable wrappedRequestMessageTypes = null); + /// + /// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. + /// + /// The remoted service interface. + /// The union of parameter types of all of the methods of the specified interface. + /// Wrapped Request Types for all Methods. + /// + /// An instance of the that can serialize the service + /// actor request message body to a messaging body for transferring over the transport. + /// + IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer( + Type serviceInterfaceType, + IEnumerable methodRequestParameterTypes, + IEnumerable wrappedRequestMessageTypes = null); - /// - /// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. - /// - /// The remoted service interface. - /// The return types of all of the methods of the specified interface. - /// Wrapped Response Types for all remoting methods. - /// - /// An instance of the that can serialize the service - /// actor response message body to a messaging body for transferring over the transport. - /// - IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer( - Type serviceInterfaceType, - IEnumerable methodReturnTypes, - IEnumerable wrappedResponseMessageTypes = null); - } -} + /// + /// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation. + /// + /// The remoted service interface. + /// The return types of all of the methods of the specified interface. + /// Wrapped Response Types for all remoting methods. + /// + /// An instance of the that can serialize the service + /// actor response message body to a messaging body for transferring over the transport. + /// + IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer( + Type serviceInterfaceType, + IEnumerable methodReturnTypes, + IEnumerable wrappedResponseMessageTypes = null); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorMessageHeaderSerializer.cs b/src/Dapr.Actors/Communication/IActorMessageHeaderSerializer.cs index 82c82f4fe..f972ae8cd 100644 --- a/src/Dapr.Actors/Communication/IActorMessageHeaderSerializer.cs +++ b/src/Dapr.Actors/Communication/IActorMessageHeaderSerializer.cs @@ -11,41 +11,40 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System.IO; +namespace Dapr.Actors.Communication; + +using System.IO; +/// +/// Represents a serializer that can serialize remoting layer message header to messaging layer header. +/// +internal interface IActorMessageHeaderSerializer +{ /// - /// Represents a serializer that can serialize remoting layer message header to messaging layer header. + /// Serializes the remoting request message header to a message header. /// - internal interface IActorMessageHeaderSerializer - { - /// - /// Serializes the remoting request message header to a message header. - /// - /// Remoting header to serialize. - /// Serialized bytes. - byte[] SerializeRequestHeader(IActorRequestMessageHeader serviceRemotingRequestMessageHeader); + /// Remoting header to serialize. + /// Serialized bytes. + byte[] SerializeRequestHeader(IActorRequestMessageHeader serviceRemotingRequestMessageHeader); - /// - /// Deserializes a request message header in to remoting header. - /// - /// Messaging layer header to be deserialized. - /// An that has the deserialized contents of the specified message header. - IActorRequestMessageHeader DeserializeRequestHeaders(Stream messageHeader); + /// + /// Deserializes a request message header in to remoting header. + /// + /// Messaging layer header to be deserialized. + /// An that has the deserialized contents of the specified message header. + IActorRequestMessageHeader DeserializeRequestHeaders(Stream messageHeader); - /// - /// Serializes the remoting response message header to a message header. - /// - /// Remoting header to serialize. - /// Serialized bytes. - byte[] SerializeResponseHeader(IActorResponseMessageHeader serviceRemotingResponseMessageHeader); + /// + /// Serializes the remoting response message header to a message header. + /// + /// Remoting header to serialize. + /// Serialized bytes. + byte[] SerializeResponseHeader(IActorResponseMessageHeader serviceRemotingResponseMessageHeader); - /// - /// Deserializes a response message header in to remoting header. - /// - /// Messaging layer header to be deserialized. - /// An that has the deserialized contents of the specified message header. - IActorResponseMessageHeader DeserializeResponseHeaders(Stream messageHeader); - } -} + /// + /// Deserializes a response message header in to remoting header. + /// + /// Messaging layer header to be deserialized. + /// An that has the deserialized contents of the specified message header. + IActorResponseMessageHeader DeserializeResponseHeaders(Stream messageHeader); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorRequestMessage.cs b/src/Dapr.Actors/Communication/IActorRequestMessage.cs index 0d850a22f..11c603123 100644 --- a/src/Dapr.Actors/Communication/IActorRequestMessage.cs +++ b/src/Dapr.Actors/Communication/IActorRequestMessage.cs @@ -11,22 +11,21 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +/// +/// Defines the interface that must be implemented for create Actor Request Message. +/// +public interface IActorRequestMessage { /// - /// Defines the interface that must be implemented for create Actor Request Message. + /// Gets the Actor Request Message Header. /// - public interface IActorRequestMessage - { - /// - /// Gets the Actor Request Message Header. - /// - /// IActorRequestMessageHeader. - IActorRequestMessageHeader GetHeader(); + /// IActorRequestMessageHeader. + IActorRequestMessageHeader GetHeader(); - /// - /// Gets the Actor Request Message Body. - /// IActorRequestMessageBody. - IActorRequestMessageBody GetBody(); - } -} + /// + /// Gets the Actor Request Message Body. + /// IActorRequestMessageBody. + IActorRequestMessageBody GetBody(); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorRequestMessageBody.cs b/src/Dapr.Actors/Communication/IActorRequestMessageBody.cs index f521227ea..c2b26872b 100644 --- a/src/Dapr.Actors/Communication/IActorRequestMessageBody.cs +++ b/src/Dapr.Actors/Communication/IActorRequestMessageBody.cs @@ -11,31 +11,30 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System; +namespace Dapr.Actors.Communication; + +using System; +/// +/// Defines the interface that must be implemented to provide Request Message Body for remoting requests . +/// This contains all the parameters remoting method has. +/// +public interface IActorRequestMessageBody +{ /// - /// Defines the interface that must be implemented to provide Request Message Body for remoting requests . - /// This contains all the parameters remoting method has. + /// This Api gets called to set remoting method parameters before serializing/dispatching the request. /// - public interface IActorRequestMessageBody - { - /// - /// This Api gets called to set remoting method parameters before serializing/dispatching the request. - /// - /// Position of the parameter in Remoting Method. - /// Parameter Name in the Remoting Method. - /// Parameter Value. - void SetParameter(int position, string parameName, object parameter); + /// Position of the parameter in Remoting Method. + /// Parameter Name in the Remoting Method. + /// Parameter Value. + void SetParameter(int position, string parameName, object parameter); - /// - /// This is used to retrive parameter from request body before dispatching to service remoting method. - /// - /// Position of the parameter in Remoting Method. - /// Parameter Name in the Remoting Method. - /// Parameter Type. - /// The parameter that is at the specified position and has the specified name. - object GetParameter(int position, string parameName, Type paramType); - } -} + /// + /// This is used to retrive parameter from request body before dispatching to service remoting method. + /// + /// Position of the parameter in Remoting Method. + /// Parameter Name in the Remoting Method. + /// Parameter Type. + /// The parameter that is at the specified position and has the specified name. + object GetParameter(int position, string parameName, Type paramType); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorRequestMessageBodySerializer.cs b/src/Dapr.Actors/Communication/IActorRequestMessageBodySerializer.cs index b58257381..3d1c96c0e 100644 --- a/src/Dapr.Actors/Communication/IActorRequestMessageBodySerializer.cs +++ b/src/Dapr.Actors/Communication/IActorRequestMessageBodySerializer.cs @@ -11,28 +11,27 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System.IO; - using System.Threading.Tasks; +namespace Dapr.Actors.Communication; + +using System.IO; +using System.Threading.Tasks; +/// +/// Defines the interface that must be implemented to provide a serializer/deserializer for remoting request message body. +/// +internal interface IActorRequestMessageBodySerializer +{ /// - /// Defines the interface that must be implemented to provide a serializer/deserializer for remoting request message body. + /// Serialize the remoting request body object to a message body that can be sent over the wire. /// - internal interface IActorRequestMessageBodySerializer - { - /// - /// Serialize the remoting request body object to a message body that can be sent over the wire. - /// - /// Actor request message body object. - /// Serialized message body. - byte[] Serialize(IActorRequestMessageBody actorRequestMessageBody); + /// Actor request message body object. + /// Serialized message body. + byte[] Serialize(IActorRequestMessageBody actorRequestMessageBody); - /// - /// Deserializes an incoming message body to actor request body object. - /// - /// Serialized message body. - /// Deserialized remoting request message body object. - ValueTask DeserializeAsync(Stream messageBody); - } -} + /// + /// Deserializes an incoming message body to actor request body object. + /// + /// Serialized message body. + /// Deserialized remoting request message body object. + ValueTask DeserializeAsync(Stream messageBody); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorRequestMessageHeader.cs b/src/Dapr.Actors/Communication/IActorRequestMessageHeader.cs index 1be097dd7..ec061c684 100644 --- a/src/Dapr.Actors/Communication/IActorRequestMessageHeader.cs +++ b/src/Dapr.Actors/Communication/IActorRequestMessageHeader.cs @@ -11,63 +11,62 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +/// +/// Specifies the headers that are sent along with a request message. +/// +public interface IActorRequestMessageHeader { /// - /// Specifies the headers that are sent along with a request message. + /// Gets or sets the actorId to which remoting request will dispatch to. /// - public interface IActorRequestMessageHeader - { - /// - /// Gets or sets the actorId to which remoting request will dispatch to. - /// - ActorId ActorId { get; set; } + ActorId ActorId { get; set; } - /// - /// Gets or sets the actorType to which remoting request will dispatch to. - /// - string ActorType { get; set; } + /// + /// Gets or sets the actorType to which remoting request will dispatch to. + /// + string ActorType { get; set; } - /// - /// Gets or sets the call context which is used to limit re-eentrancy in Actors. - /// - string CallContext { get; set; } + /// + /// Gets or sets the call context which is used to limit re-eentrancy in Actors. + /// + string CallContext { get; set; } - /// - /// Gets or sets the methodId of the remote method. - /// - /// The method id. - int MethodId { get; set; } + /// + /// Gets or sets the methodId of the remote method. + /// + /// The method id. + int MethodId { get; set; } - /// - /// Gets or sets the interface id of the remote interface. - /// - /// The interface id. - int InterfaceId { get; set; } + /// + /// Gets or sets the interface id of the remote interface. + /// + /// The interface id. + int InterfaceId { get; set; } - /// - /// Gets or sets the identifier for the remote method invocation. - /// - string InvocationId { get; set; } + /// + /// Gets or sets the identifier for the remote method invocation. + /// + string InvocationId { get; set; } - /// - /// Gets or sets the Method Name of the remoting method. - /// - string MethodName { get; set; } + /// + /// Gets or sets the Method Name of the remoting method. + /// + string MethodName { get; set; } - /// - /// Adds a new header with the specified name and value. - /// - /// The header Name. - /// The header value. - void AddHeader(string headerName, byte[] headerValue); + /// + /// Adds a new header with the specified name and value. + /// + /// The header Name. + /// The header value. + void AddHeader(string headerName, byte[] headerValue); - /// - /// Gets the header with the specified name. - /// - /// The header Name. - /// The header value. - /// true if a header with that name exists; otherwise, false. - bool TryGetHeaderValue(string headerName, out byte[] headerValue); - } -} + /// + /// Gets the header with the specified name. + /// + /// The header Name. + /// The header value. + /// true if a header with that name exists; otherwise, false. + bool TryGetHeaderValue(string headerName, out byte[] headerValue); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorResponseMessage.cs b/src/Dapr.Actors/Communication/IActorResponseMessage.cs index 29c2ad92f..189c095cd 100644 --- a/src/Dapr.Actors/Communication/IActorResponseMessage.cs +++ b/src/Dapr.Actors/Communication/IActorResponseMessage.cs @@ -11,23 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +/// +/// Defines an interface that must be implemented to provide a actor response message for remoting Api. +/// +public interface IActorResponseMessage { /// - /// Defines an interface that must be implemented to provide a actor response message for remoting Api. + /// Gets the header of the response message. /// - public interface IActorResponseMessage - { - /// - /// Gets the header of the response message. - /// - /// The header of this response message. - IActorResponseMessageHeader GetHeader(); + /// The header of this response message. + IActorResponseMessageHeader GetHeader(); - /// - /// Gets the body of the response message. - /// - /// The body of this response message. - IActorResponseMessageBody GetBody(); - } -} + /// + /// Gets the body of the response message. + /// + /// The body of this response message. + IActorResponseMessageBody GetBody(); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorResponseMessageBody.cs b/src/Dapr.Actors/Communication/IActorResponseMessageBody.cs index a7557a95e..10f57fb6a 100644 --- a/src/Dapr.Actors/Communication/IActorResponseMessageBody.cs +++ b/src/Dapr.Actors/Communication/IActorResponseMessageBody.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System; +namespace Dapr.Actors.Communication; + +using System; +/// +/// Defines the interface that must be implemented to provide Request Message Body for remoting requests . +/// This contains all the parameters remoting method has. +/// +public interface IActorResponseMessageBody +{ /// - /// Defines the interface that must be implemented to provide Request Message Body for remoting requests . - /// This contains all the parameters remoting method has. + /// Sets the response of a remoting Method in a remoting response Body. /// - public interface IActorResponseMessageBody - { - /// - /// Sets the response of a remoting Method in a remoting response Body. - /// - /// Remoting Method Response. - void Set(object response); + /// Remoting Method Response. + void Set(object response); - /// - /// Gets the response of a remoting Method from a remoting response body before sending it to Client. - /// - /// Return Type of a Remoting Method. - /// Remoting Method Response. - object Get(Type paramType); - } -} + /// + /// Gets the response of a remoting Method from a remoting response body before sending it to Client. + /// + /// Return Type of a Remoting Method. + /// Remoting Method Response. + object Get(Type paramType); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorResponseMessageBodySerializer.cs b/src/Dapr.Actors/Communication/IActorResponseMessageBodySerializer.cs index b54191f91..90cd19d27 100644 --- a/src/Dapr.Actors/Communication/IActorResponseMessageBodySerializer.cs +++ b/src/Dapr.Actors/Communication/IActorResponseMessageBodySerializer.cs @@ -11,28 +11,27 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication -{ - using System.IO; - using System.Threading.Tasks; +namespace Dapr.Actors.Communication; + +using System.IO; +using System.Threading.Tasks; +/// +/// Defines the interface that must be implemented to provide a serializer/deserializer for actor response message body. +/// +internal interface IActorResponseMessageBodySerializer +{ /// - /// Defines the interface that must be implemented to provide a serializer/deserializer for actor response message body. + /// Serialize the actor response body object to a message body that can be sent over the wire. /// - internal interface IActorResponseMessageBodySerializer - { - /// - /// Serialize the actor response body object to a message body that can be sent over the wire. - /// - /// Actor request message body object. - /// Serialized message body. - byte[] Serialize(IActorResponseMessageBody actorResponseMessageBody); + /// Actor request message body object. + /// Serialized message body. + byte[] Serialize(IActorResponseMessageBody actorResponseMessageBody); - /// - /// Deserializes an incoming message body to remoting response body object. - /// - /// Serialized message body. - /// Deserialized actor response message body object. - ValueTask DeserializeAsync(Stream messageBody); - } -} + /// + /// Deserializes an incoming message body to remoting response body object. + /// + /// Serialized message body. + /// Deserialized actor response message body object. + ValueTask DeserializeAsync(Stream messageBody); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Communication/IActorResponseMessageHeader.cs b/src/Dapr.Actors/Communication/IActorResponseMessageHeader.cs index f1f57a0c0..134783e99 100644 --- a/src/Dapr.Actors/Communication/IActorResponseMessageHeader.cs +++ b/src/Dapr.Actors/Communication/IActorResponseMessageHeader.cs @@ -11,33 +11,32 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Communication +namespace Dapr.Actors.Communication; + +/// +/// Defines an interfaces that must be implemented to provide header for remoting response message. +/// +/// +public interface IActorResponseMessageHeader { /// - /// Defines an interfaces that must be implemented to provide header for remoting response message. - /// + /// Adds a new header with the specified name and value. /// - public interface IActorResponseMessageHeader - { - /// - /// Adds a new header with the specified name and value. - /// - /// The header Name. - /// The header value. - void AddHeader(string headerName, byte[] headerValue); + /// The header Name. + /// The header value. + void AddHeader(string headerName, byte[] headerValue); - /// - /// Gets the header with the specified name. - /// - /// The header Name. - /// The header value. - /// true if a header with that name exists; otherwise, false. - bool TryGetHeaderValue(string headerName, out byte[] headerValue); + /// + /// Gets the header with the specified name. + /// + /// The header Name. + /// The header value. + /// true if a header with that name exists; otherwise, false. + bool TryGetHeaderValue(string headerName, out byte[] headerValue); - /// - /// Return true if no header exists , else false. - /// - /// true or false. - bool CheckIfItsEmpty(); - } -} + /// + /// Return true if no header exists , else false. + /// + /// true or false. + bool CheckIfItsEmpty(); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Constants.cs b/src/Dapr.Actors/Constants.cs index 038caf101..ddfbd96b0 100644 --- a/src/Dapr.Actors/Constants.cs +++ b/src/Dapr.Actors/Constants.cs @@ -11,68 +11,67 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors +namespace Dapr.Actors; + +/// +/// Contains Constant string values used by the library. +/// +internal static class Constants { + public const string RequestIdHeaderName = "X-DaprRequestId"; + public const string RequestHeaderName = "X-DaprRequestHeader"; + public const string ErrorResponseHeaderName = "X-DaprErrorResponseHeader"; + public const string ReentrancyRequestHeaderName = "Dapr-Reentrancy-Id"; + public const string TTLResponseHeaderName = "Metadata.ttlExpireTime"; + public const string Dapr = "dapr"; + public const string Config = "config"; + public const string State = "state"; + public const string Actors = "actors"; + public const string Namespace = "urn:actors"; + public const string DaprDefaultEndpoint = "127.0.0.1"; + public const string DaprDefaultPort = "3500"; + public const string DaprVersion = "v1.0"; + public const string Method = "method"; + public const string Reminders = "reminders"; + public const string Timers = "timers"; + /// - /// Contains Constant string values used by the library. + /// Gets string format for Actors state management relative url. /// - internal static class Constants - { - public const string RequestIdHeaderName = "X-DaprRequestId"; - public const string RequestHeaderName = "X-DaprRequestHeader"; - public const string ErrorResponseHeaderName = "X-DaprErrorResponseHeader"; - public const string ReentrancyRequestHeaderName = "Dapr-Reentrancy-Id"; - public const string TTLResponseHeaderName = "Metadata.ttlExpireTime"; - public const string Dapr = "dapr"; - public const string Config = "config"; - public const string State = "state"; - public const string Actors = "actors"; - public const string Namespace = "urn:actors"; - public const string DaprDefaultEndpoint = "127.0.0.1"; - public const string DaprDefaultPort = "3500"; - public const string DaprVersion = "v1.0"; - public const string Method = "method"; - public const string Reminders = "reminders"; - public const string Timers = "timers"; - - /// - /// Gets string format for Actors state management relative url. - /// - public static string ActorStateKeyRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{State}/{{2}}"; + public static string ActorStateKeyRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{State}/{{2}}"; - /// - /// Gets string format for Actors state management relative url. - /// - public static string ActorStateRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{State}"; + /// + /// Gets string format for Actors state management relative url. + /// + public static string ActorStateRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{State}"; - /// - /// Gets string format for Actors method invocation relative url. - /// - public static string ActorMethodRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{Method}/{{2}}"; + /// + /// Gets string format for Actors method invocation relative url. + /// + public static string ActorMethodRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{Method}/{{2}}"; - /// - /// Gets string format for Actors reminder registration relative url.. - /// - public static string ActorReminderRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{Reminders}/{{2}}"; + /// + /// Gets string format for Actors reminder registration relative url.. + /// + public static string ActorReminderRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{Reminders}/{{2}}"; - /// - /// Gets string format for Actors timer registration relative url.. - /// - public static string ActorTimerRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{Timers}/{{2}}"; + /// + /// Gets string format for Actors timer registration relative url.. + /// + public static string ActorTimerRelativeUrlFormat => $"{DaprVersion}/{Actors}/{{0}}/{{1}}/{Timers}/{{2}}"; - /// - /// Error code indicating there was a failure invoking an actor method. - /// - public static string ErrorActorInvokeMethod = "ERR_ACTOR_INVOKE_METHOD"; + /// + /// Error code indicating there was a failure invoking an actor method. + /// + public static string ErrorActorInvokeMethod = "ERR_ACTOR_INVOKE_METHOD"; - /// - /// Error code indicating something does not exist. - /// - public static string ErrorDoesNotExist = "ERR_DOES_NOT_EXIST"; + /// + /// Error code indicating something does not exist. + /// + public static string ErrorDoesNotExist = "ERR_DOES_NOT_EXIST"; - /// - /// Error code indicating an unknonwn/unspecified error. - /// - public static string Unknown = "UNKNOWN"; - } -} + /// + /// Error code indicating an unknonwn/unspecified error. + /// + public static string Unknown = "UNKNOWN"; +} \ No newline at end of file diff --git a/src/Dapr.Actors/DaprHttpInteractor.cs b/src/Dapr.Actors/DaprHttpInteractor.cs index 7dcbe70cc..b7b1f53a6 100644 --- a/src/Dapr.Actors/DaprHttpInteractor.cs +++ b/src/Dapr.Actors/DaprHttpInteractor.cs @@ -11,502 +11,501 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors +namespace Dapr.Actors; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Authentication; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Communication; +using Dapr.Actors.Resources; +using System.Xml; + +/// +/// Class to interact with Dapr runtime over http. +/// +internal class DaprHttpInteractor : IDaprInteractor { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Security.Authentication; - using System.Text; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Communication; - using Dapr.Actors.Resources; - using System.Xml; + private readonly JsonSerializerOptions jsonSerializerOptions = JsonSerializerDefaults.Web; + private readonly string httpEndpoint; + private readonly static HttpMessageHandler defaultHandler = new HttpClientHandler(); + private readonly HttpMessageHandler handler; + private HttpClient httpClient; + private bool disposed; + private string daprApiToken; + + private const string EXCEPTION_HEADER_TAG = "b:KeyValueOfstringbase64Binary"; + + public DaprHttpInteractor( + HttpMessageHandler clientHandler, + string httpEndpoint, + string apiToken, + TimeSpan? requestTimeout) + { + this.handler = clientHandler ?? defaultHandler; + this.httpEndpoint = httpEndpoint; + this.daprApiToken = apiToken; + this.httpClient = this.CreateHttpClient(); + this.httpClient.Timeout = requestTimeout ?? this.httpClient.Timeout; + } - /// - /// Class to interact with Dapr runtime over http. - /// - internal class DaprHttpInteractor : IDaprInteractor + public async Task> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default) { - private readonly JsonSerializerOptions jsonSerializerOptions = JsonSerializerDefaults.Web; - private readonly string httpEndpoint; - private readonly static HttpMessageHandler defaultHandler = new HttpClientHandler(); - private readonly HttpMessageHandler handler; - private HttpClient httpClient; - private bool disposed; - private string daprApiToken; - - private const string EXCEPTION_HEADER_TAG = "b:KeyValueOfstringbase64Binary"; - - public DaprHttpInteractor( - HttpMessageHandler clientHandler, - string httpEndpoint, - string apiToken, - TimeSpan? requestTimeout) - { - this.handler = clientHandler ?? defaultHandler; - this.httpEndpoint = httpEndpoint; - this.daprApiToken = apiToken; - this.httpClient = this.CreateHttpClient(); - this.httpClient.Timeout = requestTimeout ?? this.httpClient.Timeout; - } + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName); - public async Task> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default) + HttpRequestMessage RequestFunc() { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName); - - HttpRequestMessage RequestFunc() - { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Get, - }; - return request; - } - - using var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); - var stringResponse = await response.Content.ReadAsStringAsync(); - - DateTimeOffset? ttlExpireTime = null; - if (response.Headers.TryGetValues(Constants.TTLResponseHeaderName, out IEnumerable headerValues)) + var request = new HttpRequestMessage() { - var ttlExpireTimeString = headerValues.First(); - if (!string.IsNullOrEmpty(ttlExpireTimeString)) - { - ttlExpireTime = DateTime.Parse(ttlExpireTimeString, CultureInfo.InvariantCulture); - } - } - - return new ActorStateResponse(stringResponse, ttlExpireTime); + Method = HttpMethod.Get, + }; + return request; } - public Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, CancellationToken cancellationToken = default) - { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId); + using var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + var stringResponse = await response.Content.ReadAsStringAsync(); - HttpRequestMessage RequestFunc() + DateTimeOffset? ttlExpireTime = null; + if (response.Headers.TryGetValues(Constants.TTLResponseHeaderName, out IEnumerable headerValues)) + { + var ttlExpireTimeString = headerValues.First(); + if (!string.IsNullOrEmpty(ttlExpireTimeString)) { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Put, - Content = new StringContent(data), - }; - - return request; + ttlExpireTime = DateTime.Parse(ttlExpireTimeString, CultureInfo.InvariantCulture); } - - return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); } - public async Task InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken = default) - { - var requestMessageHeader = remotingRequestRequestMessage.GetHeader(); + return new ActorStateResponse(stringResponse, ttlExpireTime); + } - var actorId = requestMessageHeader.ActorId.ToString(); - var methodName = requestMessageHeader.MethodName; - var actorType = requestMessageHeader.ActorType; - var interfaceId = requestMessageHeader.InterfaceId; + public Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, CancellationToken cancellationToken = default) + { + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId); - var serializedHeader = serializersManager.GetHeaderSerializer() - .SerializeRequestHeader(remotingRequestRequestMessage.GetHeader()); + HttpRequestMessage RequestFunc() + { + var request = new HttpRequestMessage() + { + Method = HttpMethod.Put, + Content = new StringContent(data), + }; - var msgBodySeriaizer = serializersManager.GetRequestMessageBodySerializer(interfaceId, methodName); - var serializedMsgBody = msgBodySeriaizer.Serialize(remotingRequestRequestMessage.GetBody()); + return request; + } - // Send Request - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); + return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + } - HttpRequestMessage RequestFunc() - { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Put, - }; + public async Task InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken = default) + { + var requestMessageHeader = remotingRequestRequestMessage.GetHeader(); - if (serializedMsgBody != null) - { - request.Content = new ByteArrayContent(serializedMsgBody); - request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream; charset=utf-8"); - } + var actorId = requestMessageHeader.ActorId.ToString(); + var methodName = requestMessageHeader.MethodName; + var actorType = requestMessageHeader.ActorType; + var interfaceId = requestMessageHeader.InterfaceId; - request.Headers.Add(Constants.RequestHeaderName, Encoding.UTF8.GetString(serializedHeader, 0, serializedHeader.Length)); + var serializedHeader = serializersManager.GetHeaderSerializer() + .SerializeRequestHeader(remotingRequestRequestMessage.GetHeader()); - var reentrancyId = ActorReentrancyContextAccessor.ReentrancyContext; - if (reentrancyId != null) - { - request.Headers.Add(Constants.ReentrancyRequestHeaderName, reentrancyId); - } + var msgBodySeriaizer = serializersManager.GetRequestMessageBodySerializer(interfaceId, methodName); + var serializedMsgBody = msgBodySeriaizer.Serialize(remotingRequestRequestMessage.GetBody()); - return request; - } + // Send Request + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); - var retval = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); - var header = ""; - IActorResponseMessageHeader actorResponseMessageHeader = null; - if (retval != null && retval.Headers != null) + HttpRequestMessage RequestFunc() + { + var request = new HttpRequestMessage() { - if (retval.Headers.TryGetValues(Constants.ErrorResponseHeaderName, out IEnumerable headerValues)) - { - header = headerValues.First(); - // DeSerialize Actor Response Message Header - actorResponseMessageHeader = - serializersManager.GetHeaderSerializer() - .DeserializeResponseHeaders( - new MemoryStream(Encoding.ASCII.GetBytes(header))); - } - } + Method = HttpMethod.Put, + }; - // Get the http response message body content and extract out expected actor response message body - IActorResponseMessageBody actorResponseMessageBody = null; - if (retval != null && retval.Content != null) + if (serializedMsgBody != null) { - var responseMessageBody = await retval.Content.ReadAsStreamAsync(); - - // Deserialize Actor Response Message Body - // Deserialize to ActorInvokeException when there is response header otherwise normal path - var responseBodySerializer = serializersManager.GetResponseMessageBodySerializer(interfaceId, methodName); + request.Content = new ByteArrayContent(serializedMsgBody); + request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream; charset=utf-8"); + } - // actorResponseMessageHeader is not null, it means there is remote exception - if (actorResponseMessageHeader != null) - { - var isDeserialized = - ActorInvokeException.ToException( - responseMessageBody, - out var remoteMethodException); - if (isDeserialized) - { - var exceptionDetails = GetExceptionDetails(header.ToString()); - throw new ActorMethodInvocationException( - "Remote Actor Method Exception, DETAILS: " + exceptionDetails, - remoteMethodException, - false /* non transient */); - } - else - { - throw new ActorInvokeException(remoteMethodException.GetType().FullName, string.Format( - CultureInfo.InvariantCulture, - SR.ErrorDeserializationFailure, - remoteMethodException.ToString())); - } - } + request.Headers.Add(Constants.RequestHeaderName, Encoding.UTF8.GetString(serializedHeader, 0, serializedHeader.Length)); - actorResponseMessageBody = await responseBodySerializer.DeserializeAsync(responseMessageBody); + var reentrancyId = ActorReentrancyContextAccessor.ReentrancyContext; + if (reentrancyId != null) + { + request.Headers.Add(Constants.ReentrancyRequestHeaderName, reentrancyId); } - return new ActorResponseMessage(actorResponseMessageHeader, actorResponseMessageBody); + return request; } - private string GetExceptionDetails(string header) { - XmlDocument xmlHeader = new XmlDocument(); - xmlHeader.LoadXml(header); - XmlNodeList exceptionValueXML = xmlHeader.GetElementsByTagName(EXCEPTION_HEADER_TAG); - string exceptionDetails = ""; - if (exceptionValueXML != null && exceptionValueXML.Item(1) != null) + var retval = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + var header = ""; + IActorResponseMessageHeader actorResponseMessageHeader = null; + if (retval != null && retval.Headers != null) + { + if (retval.Headers.TryGetValues(Constants.ErrorResponseHeaderName, out IEnumerable headerValues)) { - exceptionDetails = exceptionValueXML.Item(1).LastChild.InnerText; + header = headerValues.First(); + // DeSerialize Actor Response Message Header + actorResponseMessageHeader = + serializersManager.GetHeaderSerializer() + .DeserializeResponseHeaders( + new MemoryStream(Encoding.ASCII.GetBytes(header))); } - var base64EncodedBytes = System.Convert.FromBase64String(exceptionDetails); - return Encoding.UTF8.GetString(base64EncodedBytes); } - public async Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default) + // Get the http response message body content and extract out expected actor response message body + IActorResponseMessageBody actorResponseMessageBody = null; + if (retval != null && retval.Content != null) { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); + var responseMessageBody = await retval.Content.ReadAsStreamAsync(); - HttpRequestMessage RequestFunc() - { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Put, - }; + // Deserialize Actor Response Message Body + // Deserialize to ActorInvokeException when there is response header otherwise normal path + var responseBodySerializer = serializersManager.GetResponseMessageBodySerializer(interfaceId, methodName); - if (jsonPayload != null) + // actorResponseMessageHeader is not null, it means there is remote exception + if (actorResponseMessageHeader != null) + { + var isDeserialized = + ActorInvokeException.ToException( + responseMessageBody, + out var remoteMethodException); + if (isDeserialized) { - request.Content = new StringContent(jsonPayload); - request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + var exceptionDetails = GetExceptionDetails(header.ToString()); + throw new ActorMethodInvocationException( + "Remote Actor Method Exception, DETAILS: " + exceptionDetails, + remoteMethodException, + false /* non transient */); } - - var reentrancyId = ActorReentrancyContextAccessor.ReentrancyContext; - if (reentrancyId != null) + else { - request.Headers.Add(Constants.ReentrancyRequestHeaderName, reentrancyId); + throw new ActorInvokeException(remoteMethodException.GetType().FullName, string.Format( + CultureInfo.InvariantCulture, + SR.ErrorDeserializationFailure, + remoteMethodException.ToString())); } - - return request; } - var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); - var stream = await response.Content.ReadAsStreamAsync(); - return stream; + actorResponseMessageBody = await responseBodySerializer.DeserializeAsync(responseMessageBody); } - public Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, CancellationToken cancellationToken = default) + return new ActorResponseMessage(actorResponseMessageHeader, actorResponseMessageBody); + } + + private string GetExceptionDetails(string header) { + XmlDocument xmlHeader = new XmlDocument(); + xmlHeader.LoadXml(header); + XmlNodeList exceptionValueXML = xmlHeader.GetElementsByTagName(EXCEPTION_HEADER_TAG); + string exceptionDetails = ""; + if (exceptionValueXML != null && exceptionValueXML.Item(1) != null) { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); + exceptionDetails = exceptionValueXML.Item(1).LastChild.InnerText; + } + var base64EncodedBytes = System.Convert.FromBase64String(exceptionDetails); + return Encoding.UTF8.GetString(base64EncodedBytes); + } + + public async Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default) + { + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); - HttpRequestMessage RequestFunc() + HttpRequestMessage RequestFunc() + { + var request = new HttpRequestMessage() { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Put, - Content = new StringContent(data, Encoding.UTF8), - }; + Method = HttpMethod.Put, + }; + if (jsonPayload != null) + { + request.Content = new StringContent(jsonPayload); request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); - return request; } - return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + var reentrancyId = ActorReentrancyContextAccessor.ReentrancyContext; + if (reentrancyId != null) + { + request.Headers.Add(Constants.ReentrancyRequestHeaderName, reentrancyId); + } + + return request; } - public async Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) - { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); + var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + var stream = await response.Content.ReadAsStreamAsync(); + return stream; + } + + public Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, CancellationToken cancellationToken = default) + { + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); - HttpRequestMessage RequestFunc() + HttpRequestMessage RequestFunc() + { + var request = new HttpRequestMessage() { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Get, - }; - return request; - } + Method = HttpMethod.Put, + Content = new StringContent(data, Encoding.UTF8), + }; - return await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + return request; } - public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) - { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); + return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + } - HttpRequestMessage RequestFunc() + public async Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) + { + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); + + HttpRequestMessage RequestFunc() + { + var request = new HttpRequestMessage() { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Delete, - }; + Method = HttpMethod.Get, + }; + return request; + } - return request; - } + return await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + } - return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); - } + public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) + { + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); - public Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, CancellationToken cancellationToken = default) + HttpRequestMessage RequestFunc() { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); - - HttpRequestMessage RequestFunc() + var request = new HttpRequestMessage() { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Put, - Content = new StringContent(data, Encoding.UTF8), - }; + Method = HttpMethod.Delete, + }; - request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); - return request; - } - - return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + return request; } - public Task UnregisterTimerAsync(string actorType, string actorId, string timerName, CancellationToken cancellationToken = default) - { - var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); + return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + } - HttpRequestMessage RequestFunc() - { - var request = new HttpRequestMessage() - { - Method = HttpMethod.Delete, - }; + public Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, CancellationToken cancellationToken = default) + { + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); - return request; - } + HttpRequestMessage RequestFunc() + { + var request = new HttpRequestMessage() + { + Method = HttpMethod.Put, + Content = new StringContent(data, Encoding.UTF8), + }; - return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + return request; } - /// - /// Sends an HTTP get request to Dapr. - /// - /// Func to create HttpRequest to send. - /// The relative URI. - /// The cancellation token. - /// The payload of the GET response. - internal async Task SendAsync( - Func requestFunc, - string relativeUri, - CancellationToken cancellationToken) + return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + } + + public Task UnregisterTimerAsync(string actorType, string actorId, string timerName, CancellationToken cancellationToken = default) + { + var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); + + HttpRequestMessage RequestFunc() { - return await this.SendAsyncHandleUnsuccessfulResponse(requestFunc, relativeUri, cancellationToken); + var request = new HttpRequestMessage() + { + Method = HttpMethod.Delete, + }; + + return request; } - /// - /// Sends an HTTP get request to Dapr and returns the result as raw JSON. - /// - /// Func to create HttpRequest to send. - /// The relative URI. - /// The cancellation token. - /// The payload of the GET response as string. - internal async Task SendAsyncGetResponseAsRawJson( - Func requestFunc, - string relativeUri, - CancellationToken cancellationToken) - { - using var response = await this.SendAsyncHandleUnsuccessfulResponse(requestFunc, relativeUri, cancellationToken); - var retValue = default(string); + return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); + } - if (response != null && response.Content != null) - { - retValue = await response.Content.ReadAsStringAsync(); - } + /// + /// Sends an HTTP get request to Dapr. + /// + /// Func to create HttpRequest to send. + /// The relative URI. + /// The cancellation token. + /// The payload of the GET response. + internal async Task SendAsync( + Func requestFunc, + string relativeUri, + CancellationToken cancellationToken) + { + return await this.SendAsyncHandleUnsuccessfulResponse(requestFunc, relativeUri, cancellationToken); + } + + /// + /// Sends an HTTP get request to Dapr and returns the result as raw JSON. + /// + /// Func to create HttpRequest to send. + /// The relative URI. + /// The cancellation token. + /// The payload of the GET response as string. + internal async Task SendAsyncGetResponseAsRawJson( + Func requestFunc, + string relativeUri, + CancellationToken cancellationToken) + { + using var response = await this.SendAsyncHandleUnsuccessfulResponse(requestFunc, relativeUri, cancellationToken); + var retValue = default(string); - return retValue; + if (response != null && response.Content != null) + { + retValue = await response.Content.ReadAsStringAsync(); } - /// - /// Disposes resources. - /// - /// False values indicates the method is being called by the runtime, true value indicates the method is called by the user code. - protected virtual void Dispose(bool disposing) + return retValue; + } + + /// + /// Disposes resources. + /// + /// False values indicates the method is being called by the runtime, true value indicates the method is called by the user code. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed) { - if (!this.disposed) + if (disposing) { - if (disposing) - { - this.httpClient.Dispose(); - this.httpClient = null; - } - - this.disposed = true; + this.httpClient.Dispose(); + this.httpClient = null; } + + this.disposed = true; } + } - /// - /// Sends an HTTP get request to cluster http gateway. - /// - /// Func to create HttpRequest to send. - /// The relative URI. - /// The cancellation token. - /// The payload of the GET response. - private async Task SendAsyncHandleUnsuccessfulResponse( - Func requestFunc, - string relativeUri, - CancellationToken cancellationToken) + /// + /// Sends an HTTP get request to cluster http gateway. + /// + /// Func to create HttpRequest to send. + /// The relative URI. + /// The cancellation token. + /// The payload of the GET response. + private async Task SendAsyncHandleUnsuccessfulResponse( + Func requestFunc, + string relativeUri, + CancellationToken cancellationToken) + { + HttpRequestMessage FinalRequestFunc() { - HttpRequestMessage FinalRequestFunc() - { - var request = requestFunc.Invoke(); - request.RequestUri = new Uri($"{this.httpEndpoint}/{relativeUri}"); - return request; - } + var request = requestFunc.Invoke(); + request.RequestUri = new Uri($"{this.httpEndpoint}/{relativeUri}"); + return request; + } - var response = await this.SendAsyncHandleSecurityExceptions(FinalRequestFunc, cancellationToken); + var response = await this.SendAsyncHandleSecurityExceptions(FinalRequestFunc, cancellationToken); - if (response.IsSuccessStatusCode) - { - return response; - } + if (response.IsSuccessStatusCode) + { + return response; + } - // TODO Log Unsuccessful Response. + // TODO Log Unsuccessful Response. - // Try to get Error Information if present in response body. - if (response.Content != null) - { - DaprError error = null; + // Try to get Error Information if present in response body. + if (response.Content != null) + { + DaprError error = null; - try - { - var contentStream = await response.Content.ReadAsStreamAsync(); - if (contentStream.Length != 0) - { - error = await JsonSerializer.DeserializeAsync(contentStream, jsonSerializerOptions); - } - } - catch (Exception ex) + try + { + var contentStream = await response.Content.ReadAsStreamAsync(); + if (contentStream.Length != 0) { - throw new DaprApiException(string.Format("ServerErrorNoMeaningFulResponse", response.StatusCode), ex); + error = await JsonSerializer.DeserializeAsync(contentStream, jsonSerializerOptions); } + } + catch (Exception ex) + { + throw new DaprApiException(string.Format("ServerErrorNoMeaningFulResponse", response.StatusCode), ex); + } - if (error != null) - { - throw new DaprApiException(error.Message, error.ErrorCode, false); - } - else + if (error != null) + { + throw new DaprApiException(error.Message, error.ErrorCode, false); + } + else + { + // Handle NotFound 404, without any ErrorCode. + if (response.StatusCode.Equals(HttpStatusCode.NotFound)) { - // Handle NotFound 404, without any ErrorCode. - if (response.StatusCode.Equals(HttpStatusCode.NotFound)) - { - throw new DaprApiException("ErrorMessageHTTP404", Constants.ErrorDoesNotExist, false); - } + throw new DaprApiException("ErrorMessageHTTP404", Constants.ErrorDoesNotExist, false); } } - - // Couldn't determine Error information from response., throw exception with status code. - throw new DaprApiException(string.Format("ServerErrorNoMeaningFulResponse", response.StatusCode)); } - /// - /// Send an HTTP request as an asynchronous operation using HttpClient and handles security exceptions. - /// If UserCode to Dapr calls are over https in future, this method will handle refreshing security. - /// - /// Delegate to get HTTP request message to send. - /// The cancellation token to cancel operation. - /// The task object representing the asynchronous operation. - private async Task SendAsyncHandleSecurityExceptions( - Func requestFunc, - CancellationToken cancellationToken) - { - HttpResponseMessage response; + // Couldn't determine Error information from response., throw exception with status code. + throw new DaprApiException(string.Format("ServerErrorNoMeaningFulResponse", response.StatusCode)); + } - // Get the request using the Func as same request cannot be resent when retries are implemented. - using var request = requestFunc.Invoke(); + /// + /// Send an HTTP request as an asynchronous operation using HttpClient and handles security exceptions. + /// If UserCode to Dapr calls are over https in future, this method will handle refreshing security. + /// + /// Delegate to get HTTP request message to send. + /// The cancellation token to cancel operation. + /// The task object representing the asynchronous operation. + private async Task SendAsyncHandleSecurityExceptions( + Func requestFunc, + CancellationToken cancellationToken) + { + HttpResponseMessage response; - // add token for dapr api token based authentication - this.AddDaprApiTokenHeader(request); + // Get the request using the Func as same request cannot be resent when retries are implemented. + using var request = requestFunc.Invoke(); - response = await this.httpClient.SendAsync(request, cancellationToken); + // add token for dapr api token based authentication + this.AddDaprApiTokenHeader(request); - if (!response.IsSuccessStatusCode) + response = await this.httpClient.SendAsync(request, cancellationToken); + + if (!response.IsSuccessStatusCode) + { + // RefreshSecurity Settings and try again, + if (response.StatusCode == HttpStatusCode.Forbidden || + response.StatusCode == HttpStatusCode.Unauthorized) { - // RefreshSecurity Settings and try again, - if (response.StatusCode == HttpStatusCode.Forbidden || - response.StatusCode == HttpStatusCode.Unauthorized) - { - // TODO Log - throw new AuthenticationException("Invalid client credentials"); - } - else - { - return response; - } + // TODO Log + throw new AuthenticationException("Invalid client credentials"); } else { return response; } } - - private HttpClient CreateHttpClient() + else { - return new HttpClient(this.handler, false); + return response; } + } + + private HttpClient CreateHttpClient() + { + return new HttpClient(this.handler, false); + } - private void AddDaprApiTokenHeader(HttpRequestMessage request) + private void AddDaprApiTokenHeader(HttpRequestMessage request) + { + if (!string.IsNullOrWhiteSpace(this.daprApiToken)) { - if (!string.IsNullOrWhiteSpace(this.daprApiToken)) - { - request.Headers.Add("dapr-api-token", this.daprApiToken); - return; - } + request.Headers.Add("dapr-api-token", this.daprApiToken); + return; } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Description/ActorInterfaceDescription.cs b/src/Dapr.Actors/Description/ActorInterfaceDescription.cs index 69885984a..02b4f84a6 100644 --- a/src/Dapr.Actors/Description/ActorInterfaceDescription.cs +++ b/src/Dapr.Actors/Description/ActorInterfaceDescription.cs @@ -11,73 +11,72 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +using System; +using System.Globalization; +using System.Reflection; +using Dapr.Actors; +using Dapr.Actors.Resources; +using Dapr.Actors.Runtime; + +internal class ActorInterfaceDescription : InterfaceDescription { - using System; - using System.Globalization; - using System.Reflection; - using Dapr.Actors; - using Dapr.Actors.Resources; - using Dapr.Actors.Runtime; + private ActorInterfaceDescription(Type actorInterfaceType, bool useCRCIdGeneration) + : base("actor", actorInterfaceType, useCRCIdGeneration, MethodReturnCheck.EnsureReturnsTask) + { + } - internal class ActorInterfaceDescription : InterfaceDescription + public static ActorInterfaceDescription Create(Type actorInterfaceType) { - private ActorInterfaceDescription(Type actorInterfaceType, bool useCRCIdGeneration) - : base("actor", actorInterfaceType, useCRCIdGeneration, MethodReturnCheck.EnsureReturnsTask) - { - } + EnsureActorInterface(actorInterfaceType); + return new ActorInterfaceDescription(actorInterfaceType, false); + } - public static ActorInterfaceDescription Create(Type actorInterfaceType) - { - EnsureActorInterface(actorInterfaceType); - return new ActorInterfaceDescription(actorInterfaceType, false); - } + public static ActorInterfaceDescription CreateUsingCRCId(Type actorInterfaceType) + { + EnsureActorInterface(actorInterfaceType); - public static ActorInterfaceDescription CreateUsingCRCId(Type actorInterfaceType) - { - EnsureActorInterface(actorInterfaceType); + return new ActorInterfaceDescription(actorInterfaceType, true); + } - return new ActorInterfaceDescription(actorInterfaceType, true); + private static void EnsureActorInterface(Type actorInterfaceType) + { + if (!actorInterfaceType.GetTypeInfo().IsInterface) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorNotAnActorInterface_InterfaceCheck, + actorInterfaceType.FullName, + typeof(IActor).FullName), + "actorInterfaceType"); } - private static void EnsureActorInterface(Type actorInterfaceType) + var nonActorParentInterface = actorInterfaceType.GetNonActorParentType(); + if (nonActorParentInterface != null) { - if (!actorInterfaceType.GetTypeInfo().IsInterface) + if (nonActorParentInterface == actorInterfaceType) { throw new ArgumentException( string.Format( CultureInfo.CurrentCulture, - SR.ErrorNotAnActorInterface_InterfaceCheck, + SR.ErrorNotAnActorInterface_DerivationCheck1, actorInterfaceType.FullName, typeof(IActor).FullName), "actorInterfaceType"); } - - var nonActorParentInterface = actorInterfaceType.GetNonActorParentType(); - if (nonActorParentInterface != null) + else { - if (nonActorParentInterface == actorInterfaceType) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorNotAnActorInterface_DerivationCheck1, - actorInterfaceType.FullName, - typeof(IActor).FullName), - "actorInterfaceType"); - } - else - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorNotAnActorInterface_DerivationCheck2, - actorInterfaceType.FullName, - nonActorParentInterface.FullName, - typeof(IActor).FullName), - "actorInterfaceType"); - } + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorNotAnActorInterface_DerivationCheck2, + actorInterfaceType.FullName, + nonActorParentInterface.FullName, + typeof(IActor).FullName), + "actorInterfaceType"); } } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Description/InterfaceDescription.cs b/src/Dapr.Actors/Description/InterfaceDescription.cs index 898a70eda..41c8a145f 100644 --- a/src/Dapr.Actors/Description/InterfaceDescription.cs +++ b/src/Dapr.Actors/Description/InterfaceDescription.cs @@ -11,224 +11,223 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using Dapr.Actors.Common; +using Dapr.Actors.Resources; + +internal abstract class InterfaceDescription { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Reflection; - using Dapr.Actors.Common; - using Dapr.Actors.Resources; - - internal abstract class InterfaceDescription + private readonly Type remotedInterfaceType; + private readonly bool useCRCIdGeneration; + private readonly int interfaceId; + private readonly int interfaceIdV1; + + private readonly MethodDescription[] methods; + + protected InterfaceDescription( + string remotedInterfaceKindName, + Type remotedInterfaceType, + bool useCRCIdGeneration, + MethodReturnCheck methodReturnCheck = MethodReturnCheck.EnsureReturnsTask) { - private readonly Type remotedInterfaceType; - private readonly bool useCRCIdGeneration; - private readonly int interfaceId; - private readonly int interfaceIdV1; - - private readonly MethodDescription[] methods; - - protected InterfaceDescription( - string remotedInterfaceKindName, - Type remotedInterfaceType, - bool useCRCIdGeneration, - MethodReturnCheck methodReturnCheck = MethodReturnCheck.EnsureReturnsTask) - { - EnsureNotGeneric(remotedInterfaceKindName, remotedInterfaceType); - - this.remotedInterfaceType = remotedInterfaceType; - this.useCRCIdGeneration = useCRCIdGeneration; - if (this.useCRCIdGeneration) - { - this.interfaceId = IdUtil.ComputeIdWithCRC(remotedInterfaceType); + EnsureNotGeneric(remotedInterfaceKindName, remotedInterfaceType); - // This is needed for backward compatibility support to V1 Stack like ActorEventproxy - this.interfaceIdV1 = IdUtil.ComputeId(remotedInterfaceType); - } - else - { - this.interfaceId = IdUtil.ComputeId(remotedInterfaceType); - } + this.remotedInterfaceType = remotedInterfaceType; + this.useCRCIdGeneration = useCRCIdGeneration; + if (this.useCRCIdGeneration) + { + this.interfaceId = IdUtil.ComputeIdWithCRC(remotedInterfaceType); - this.methods = GetMethodDescriptions(remotedInterfaceKindName, remotedInterfaceType, methodReturnCheck, useCRCIdGeneration); + // This is needed for backward compatibility support to V1 Stack like ActorEventproxy + this.interfaceIdV1 = IdUtil.ComputeId(remotedInterfaceType); } - - public int V1Id + else { - get { return this.interfaceIdV1; } + this.interfaceId = IdUtil.ComputeId(remotedInterfaceType); } - public int Id - { - get { return this.interfaceId; } - } + this.methods = GetMethodDescriptions(remotedInterfaceKindName, remotedInterfaceType, methodReturnCheck, useCRCIdGeneration); + } - public Type InterfaceType - { - get { return this.remotedInterfaceType; } - } + public int V1Id + { + get { return this.interfaceIdV1; } + } - public MethodDescription[] Methods - { - get { return this.methods; } - } + public int Id + { + get { return this.interfaceId; } + } + + public Type InterfaceType + { + get { return this.remotedInterfaceType; } + } - private static void EnsureNotGeneric( - string remotedInterfaceKindName, - Type remotedInterfaceType) + public MethodDescription[] Methods + { + get { return this.methods; } + } + + private static void EnsureNotGeneric( + string remotedInterfaceKindName, + Type remotedInterfaceType) + { + if (remotedInterfaceType.GetTypeInfo().IsGenericType || + remotedInterfaceType.GetTypeInfo().IsGenericTypeDefinition) { - if (remotedInterfaceType.GetTypeInfo().IsGenericType || - remotedInterfaceType.GetTypeInfo().IsGenericTypeDefinition) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorRemotedInterfaceIsGeneric, - remotedInterfaceKindName, - remotedInterfaceType.FullName), - remotedInterfaceKindName + "InterfaceType"); - } + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorRemotedInterfaceIsGeneric, + remotedInterfaceKindName, + remotedInterfaceType.FullName), + remotedInterfaceKindName + "InterfaceType"); } + } - private static MethodDescription[] GetMethodDescriptions( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodReturnCheck methodReturnCheck, - bool useCRCIdGeneration) + private static MethodDescription[] GetMethodDescriptions( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodReturnCheck methodReturnCheck, + bool useCRCIdGeneration) + { + EnsureValidMethods(remotedInterfaceKindName, remotedInterfaceType, methodReturnCheck); + var methods = remotedInterfaceType.GetMethods(); + var methodDescriptions = new MethodDescription[methods.Length]; + for (var i = 0; i < methods.Length; i++) { - EnsureValidMethods(remotedInterfaceKindName, remotedInterfaceType, methodReturnCheck); - var methods = remotedInterfaceType.GetMethods(); - var methodDescriptions = new MethodDescription[methods.Length]; - for (var i = 0; i < methods.Length; i++) - { - methodDescriptions[i] = MethodDescription.Create(remotedInterfaceKindName, methods[i], useCRCIdGeneration); - } - - return methodDescriptions; + methodDescriptions[i] = MethodDescription.Create(remotedInterfaceKindName, methods[i], useCRCIdGeneration); } - private static void EnsureValidMethods( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodReturnCheck methodReturnCheck) + return methodDescriptions; + } + + private static void EnsureValidMethods( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodReturnCheck methodReturnCheck) + { + var methodNameSet = new HashSet(); + foreach (var m in remotedInterfaceType.GetMethods()) { - var methodNameSet = new HashSet(); - foreach (var m in remotedInterfaceType.GetMethods()) + EnsureNotOverloaded(remotedInterfaceKindName, remotedInterfaceType, m, methodNameSet); + EnsureNotGeneric(remotedInterfaceKindName, remotedInterfaceType, m); + EnsureNotVariableArgs(remotedInterfaceKindName, remotedInterfaceType, m); + + if (methodReturnCheck == MethodReturnCheck.EnsureReturnsTask) { - EnsureNotOverloaded(remotedInterfaceKindName, remotedInterfaceType, m, methodNameSet); - EnsureNotGeneric(remotedInterfaceKindName, remotedInterfaceType, m); - EnsureNotVariableArgs(remotedInterfaceKindName, remotedInterfaceType, m); - - if (methodReturnCheck == MethodReturnCheck.EnsureReturnsTask) - { - EnsureReturnsTask(remotedInterfaceKindName, remotedInterfaceType, m); - } - - if (methodReturnCheck == MethodReturnCheck.EnsureReturnsVoid) - { - EnsureReturnsVoid(remotedInterfaceKindName, remotedInterfaceType, m); - } + EnsureReturnsTask(remotedInterfaceKindName, remotedInterfaceType, m); } - } - private static void EnsureNotOverloaded( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo, - ISet methodNameSet) - { - if (methodNameSet.Contains(methodInfo.Name)) + if (methodReturnCheck == MethodReturnCheck.EnsureReturnsVoid) { - ThrowArgumentExceptionForMethodChecks( - remotedInterfaceKindName, - remotedInterfaceType, - methodInfo, - SR.ErrorRemotedMethodsIsOverloaded); + EnsureReturnsVoid(remotedInterfaceKindName, remotedInterfaceType, m); } - - methodNameSet.Add((methodInfo.Name)); } + } - private static void EnsureNotGeneric( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo) + private static void EnsureNotOverloaded( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo, + ISet methodNameSet) + { + if (methodNameSet.Contains(methodInfo.Name)) { - if (methodInfo.IsGenericMethod) - { - ThrowArgumentExceptionForMethodChecks( - remotedInterfaceKindName, - remotedInterfaceType, - methodInfo, - SR.ErrorRemotedMethodHasGenerics); - } + ThrowArgumentExceptionForMethodChecks( + remotedInterfaceKindName, + remotedInterfaceType, + methodInfo, + SR.ErrorRemotedMethodsIsOverloaded); } - private static void EnsureNotVariableArgs( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo) + methodNameSet.Add((methodInfo.Name)); + } + + private static void EnsureNotGeneric( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo) + { + if (methodInfo.IsGenericMethod) { - if (methodInfo.CallingConvention.HasFlag(CallingConventions.VarArgs)) - { - ThrowArgumentExceptionForMethodChecks( - remotedInterfaceKindName, - remotedInterfaceType, - methodInfo, - SR.ErrorRemotedMethodHasVarArgs); - } + ThrowArgumentExceptionForMethodChecks( + remotedInterfaceKindName, + remotedInterfaceType, + methodInfo, + SR.ErrorRemotedMethodHasGenerics); } + } - private static void EnsureReturnsTask( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo) + private static void EnsureNotVariableArgs( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo) + { + if (methodInfo.CallingConvention.HasFlag(CallingConventions.VarArgs)) { - if (!TypeUtility.IsTaskType(methodInfo.ReturnType)) - { - ThrowArgumentExceptionForMethodChecks( - remotedInterfaceKindName, - remotedInterfaceType, - methodInfo, - SR.ErrorRemotedMethodDoesNotReturnTask); - } + ThrowArgumentExceptionForMethodChecks( + remotedInterfaceKindName, + remotedInterfaceType, + methodInfo, + SR.ErrorRemotedMethodHasVarArgs); } + } - private static void EnsureReturnsVoid( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo) + private static void EnsureReturnsTask( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo) + { + if (!TypeUtility.IsTaskType(methodInfo.ReturnType)) { - if (!TypeUtility.IsVoidType(methodInfo.ReturnType)) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorRemotedMethodDoesNotReturnVoid, - remotedInterfaceKindName, - methodInfo.Name, - remotedInterfaceType.FullName, - methodInfo.ReturnType.FullName, - typeof(void)), - remotedInterfaceKindName + "InterfaceType"); - } + ThrowArgumentExceptionForMethodChecks( + remotedInterfaceKindName, + remotedInterfaceType, + methodInfo, + SR.ErrorRemotedMethodDoesNotReturnTask); } + } - private static void ThrowArgumentExceptionForMethodChecks( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo, - string resourceName) + private static void EnsureReturnsVoid( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo) + { + if (!TypeUtility.IsVoidType(methodInfo.ReturnType)) { throw new ArgumentException( string.Format( CultureInfo.CurrentCulture, - resourceName, + SR.ErrorRemotedMethodDoesNotReturnVoid, remotedInterfaceKindName, methodInfo.Name, - remotedInterfaceType.FullName), + remotedInterfaceType.FullName, + methodInfo.ReturnType.FullName, + typeof(void)), remotedInterfaceKindName + "InterfaceType"); } } -} + + private static void ThrowArgumentExceptionForMethodChecks( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo, + string resourceName) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + resourceName, + remotedInterfaceKindName, + methodInfo.Name, + remotedInterfaceType.FullName), + remotedInterfaceKindName + "InterfaceType"); + } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Description/MethodArgumentDescription.cs b/src/Dapr.Actors/Description/MethodArgumentDescription.cs index 7685c3615..b7eaae30b 100644 --- a/src/Dapr.Actors/Description/MethodArgumentDescription.cs +++ b/src/Dapr.Actors/Description/MethodArgumentDescription.cs @@ -11,91 +11,90 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +using System; +using System.Globalization; +using System.Reflection; +using Dapr.Actors.Resources; + +internal sealed class MethodArgumentDescription { - using System; - using System.Globalization; - using System.Reflection; - using Dapr.Actors.Resources; + private readonly ParameterInfo parameterInfo; - internal sealed class MethodArgumentDescription + private MethodArgumentDescription(ParameterInfo parameterInfo) { - private readonly ParameterInfo parameterInfo; - - private MethodArgumentDescription(ParameterInfo parameterInfo) - { - this.parameterInfo = parameterInfo; - } + this.parameterInfo = parameterInfo; + } - public string Name - { - get { return this.parameterInfo.Name; } - } + public string Name + { + get { return this.parameterInfo.Name; } + } - public Type ArgumentType - { - get { return this.parameterInfo.ParameterType; } - } + public Type ArgumentType + { + get { return this.parameterInfo.ParameterType; } + } - internal static MethodArgumentDescription Create(string remotedInterfaceKindName, MethodInfo methodInfo, ParameterInfo parameter) - { - var remotedInterfaceType = methodInfo.DeclaringType; - EnsureNotOutRefOptional(remotedInterfaceKindName, remotedInterfaceType, methodInfo, parameter); - EnsureNotVariableLength(remotedInterfaceKindName, remotedInterfaceType, methodInfo, parameter); + internal static MethodArgumentDescription Create(string remotedInterfaceKindName, MethodInfo methodInfo, ParameterInfo parameter) + { + var remotedInterfaceType = methodInfo.DeclaringType; + EnsureNotOutRefOptional(remotedInterfaceKindName, remotedInterfaceType, methodInfo, parameter); + EnsureNotVariableLength(remotedInterfaceKindName, remotedInterfaceType, methodInfo, parameter); - return new MethodArgumentDescription(parameter); - } + return new MethodArgumentDescription(parameter); + } - private static void EnsureNotVariableLength( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo, - ParameterInfo param) + private static void EnsureNotVariableLength( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo, + ParameterInfo param) + { + if (param.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0) { - if (param.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0) - { - ThrowArgumentExceptionForParamChecks( - remotedInterfaceKindName, - remotedInterfaceType, - methodInfo, - param, - SR.ErrorRemotedMethodHasVarArgParameter); - } + ThrowArgumentExceptionForParamChecks( + remotedInterfaceKindName, + remotedInterfaceType, + methodInfo, + param, + SR.ErrorRemotedMethodHasVarArgParameter); } + } - private static void EnsureNotOutRefOptional( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo, - ParameterInfo param) + private static void EnsureNotOutRefOptional( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo, + ParameterInfo param) + { + if (param.IsOut || param.IsIn || param.IsOptional) { - if (param.IsOut || param.IsIn || param.IsOptional) - { - ThrowArgumentExceptionForParamChecks( - remotedInterfaceKindName, - remotedInterfaceType, - methodInfo, - param, - SR.ErrorRemotedMethodHasOutRefOptionalParameter); - } + ThrowArgumentExceptionForParamChecks( + remotedInterfaceKindName, + remotedInterfaceType, + methodInfo, + param, + SR.ErrorRemotedMethodHasOutRefOptionalParameter); } + } - private static void ThrowArgumentExceptionForParamChecks( - string remotedInterfaceKindName, - Type remotedInterfaceType, - MethodInfo methodInfo, - ParameterInfo param, - string resourceName) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - resourceName, - remotedInterfaceKindName, - methodInfo.Name, - remotedInterfaceType.FullName, - param.Name), - remotedInterfaceKindName + "InterfaceType"); - } + private static void ThrowArgumentExceptionForParamChecks( + string remotedInterfaceKindName, + Type remotedInterfaceType, + MethodInfo methodInfo, + ParameterInfo param, + string resourceName) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + resourceName, + remotedInterfaceKindName, + methodInfo.Name, + remotedInterfaceType.FullName, + param.Name), + remotedInterfaceKindName + "InterfaceType"); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Description/MethodDescription.cs b/src/Dapr.Actors/Description/MethodDescription.cs index aca6e223b..3ee7b0bfb 100644 --- a/src/Dapr.Actors/Description/MethodDescription.cs +++ b/src/Dapr.Actors/Description/MethodDescription.cs @@ -11,97 +11,96 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Threading; +using Dapr.Actors.Common; +using Dapr.Actors.Resources; + +internal class MethodDescription { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Reflection; - using System.Threading; - using Dapr.Actors.Common; - using Dapr.Actors.Resources; + private readonly bool useCRCIdGeneration; - internal class MethodDescription + private MethodDescription( + MethodInfo methodInfo, + MethodArgumentDescription[] arguments, + bool hasCancellationToken, + bool useCRCIdGeneration) { - private readonly bool useCRCIdGeneration; - - private MethodDescription( - MethodInfo methodInfo, - MethodArgumentDescription[] arguments, - bool hasCancellationToken, - bool useCRCIdGeneration) + this.MethodInfo = methodInfo; + this.useCRCIdGeneration = useCRCIdGeneration; + if (this.useCRCIdGeneration) { - this.MethodInfo = methodInfo; - this.useCRCIdGeneration = useCRCIdGeneration; - if (this.useCRCIdGeneration) - { - this.Id = IdUtil.ComputeIdWithCRC(methodInfo); - } - else - { - this.Id = IdUtil.ComputeId(methodInfo); - } - - this.Arguments = arguments; - this.HasCancellationToken = hasCancellationToken; + this.Id = IdUtil.ComputeIdWithCRC(methodInfo); } - - public int Id { get; } - - public string Name + else { - get { return this.MethodInfo.Name; } + this.Id = IdUtil.ComputeId(methodInfo); } - public Type ReturnType - { - get { return this.MethodInfo.ReturnType; } - } + this.Arguments = arguments; + this.HasCancellationToken = hasCancellationToken; + } - public bool HasCancellationToken { get; } + public int Id { get; } - public MethodArgumentDescription[] Arguments { get; } + public string Name + { + get { return this.MethodInfo.Name; } + } - public MethodInfo MethodInfo { get; } + public Type ReturnType + { + get { return this.MethodInfo.ReturnType; } + } - internal static MethodDescription Create(string remotedInterfaceKindName, MethodInfo methodInfo, bool useCRCIdGeneration) - { - var parameters = methodInfo.GetParameters(); - var argumentList = new List(parameters.Length); - var hasCancellationToken = false; + public bool HasCancellationToken { get; } - foreach (var param in parameters) - { - if (hasCancellationToken) - { - // If the method has a cancellation token, then it must be the last argument. - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorRemotedMethodCancellationTokenOutOfOrder, - remotedInterfaceKindName, - methodInfo.Name, - methodInfo.DeclaringType.FullName, - param.Name, - typeof(CancellationToken)), - remotedInterfaceKindName + "InterfaceType"); - } + public MethodArgumentDescription[] Arguments { get; } - if (param.ParameterType == typeof(CancellationToken)) - { - hasCancellationToken = true; - } - else - { - argumentList.Add(MethodArgumentDescription.Create(remotedInterfaceKindName, methodInfo, param)); - } + public MethodInfo MethodInfo { get; } + + internal static MethodDescription Create(string remotedInterfaceKindName, MethodInfo methodInfo, bool useCRCIdGeneration) + { + var parameters = methodInfo.GetParameters(); + var argumentList = new List(parameters.Length); + var hasCancellationToken = false; + + foreach (var param in parameters) + { + if (hasCancellationToken) + { + // If the method has a cancellation token, then it must be the last argument. + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorRemotedMethodCancellationTokenOutOfOrder, + remotedInterfaceKindName, + methodInfo.Name, + methodInfo.DeclaringType.FullName, + param.Name, + typeof(CancellationToken)), + remotedInterfaceKindName + "InterfaceType"); } - return new MethodDescription( - methodInfo, - argumentList.ToArray(), - hasCancellationToken, - useCRCIdGeneration); + if (param.ParameterType == typeof(CancellationToken)) + { + hasCancellationToken = true; + } + else + { + argumentList.Add(MethodArgumentDescription.Create(remotedInterfaceKindName, methodInfo, param)); + } } + + return new MethodDescription( + methodInfo, + argumentList.ToArray(), + hasCancellationToken, + useCRCIdGeneration); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Description/MethodReturnCheck.cs b/src/Dapr.Actors/Description/MethodReturnCheck.cs index 2aa4205a6..d3693e7cf 100644 --- a/src/Dapr.Actors/Description/MethodReturnCheck.cs +++ b/src/Dapr.Actors/Description/MethodReturnCheck.cs @@ -11,11 +11,10 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +internal enum MethodReturnCheck { - internal enum MethodReturnCheck - { - EnsureReturnsTask, - EnsureReturnsVoid, - } -} + EnsureReturnsTask, + EnsureReturnsVoid, +} \ No newline at end of file diff --git a/src/Dapr.Actors/Description/TypeUtility.cs b/src/Dapr.Actors/Description/TypeUtility.cs index 4890d1ca9..d4a23ffac 100644 --- a/src/Dapr.Actors/Description/TypeUtility.cs +++ b/src/Dapr.Actors/Description/TypeUtility.cs @@ -11,23 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Description -{ - using System; - using System.Reflection; - using System.Threading.Tasks; +namespace Dapr.Actors.Description; + +using System; +using System.Reflection; +using System.Threading.Tasks; - internal static class TypeUtility +internal static class TypeUtility +{ + public static bool IsTaskType(Type type) { - public static bool IsTaskType(Type type) - { - return ((type == typeof(Task)) || - (type.GetTypeInfo().IsGenericType && (type.GetGenericTypeDefinition() == typeof(Task<>)))); - } + return ((type == typeof(Task)) || + (type.GetTypeInfo().IsGenericType && (type.GetGenericTypeDefinition() == typeof(Task<>)))); + } - public static bool IsVoidType(Type type) - { - return ((type == typeof(void)) || (type == null)); - } + public static bool IsVoidType(Type type) + { + return ((type == typeof(void)) || (type == null)); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Helper.cs b/src/Dapr.Actors/Helper.cs index 9cd4c602b..06afaac23 100644 --- a/src/Dapr.Actors/Helper.cs +++ b/src/Dapr.Actors/Helper.cs @@ -11,28 +11,27 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors -{ - using System; - using System.Globalization; - using Dapr.Actors.Communication; +namespace Dapr.Actors; + +using System; +using System.Globalization; +using Dapr.Actors.Communication; - internal class Helper +internal class Helper +{ + public static string GetCallContext() { - public static string GetCallContext() + if (ActorLogicalCallContext.TryGet(out var callContextValue)) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0}{1}", + callContextValue, + Guid.NewGuid().ToString()); + } + else { - if (ActorLogicalCallContext.TryGet(out var callContextValue)) - { - return string.Format( - CultureInfo.InvariantCulture, - "{0}{1}", - callContextValue, - Guid.NewGuid().ToString()); - } - else - { - return Guid.NewGuid().ToString(); - } + return Guid.NewGuid().ToString(); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/IActor.cs b/src/Dapr.Actors/IActor.cs index 652d88afd..6e6ce1242 100644 --- a/src/Dapr.Actors/IActor.cs +++ b/src/Dapr.Actors/IActor.cs @@ -11,12 +11,11 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors +namespace Dapr.Actors; + +/// +/// Base interface for inheriting reliable actor interfaces. +/// +public interface IActor { - /// - /// Base interface for inheriting reliable actor interfaces. - /// - public interface IActor - { - } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/IActorReference.cs b/src/Dapr.Actors/IActorReference.cs index 12a7c103d..560f4a8b3 100644 --- a/src/Dapr.Actors/IActorReference.cs +++ b/src/Dapr.Actors/IActorReference.cs @@ -11,23 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors -{ - using System; - using Dapr.Actors.Client; +namespace Dapr.Actors; + +using System; +using Dapr.Actors.Client; +/// +/// Interface for ActorReference. +/// +internal interface IActorReference +{ /// - /// Interface for ActorReference. + /// Creates an that implements an actor interface for the actor using the + /// + /// method. /// - internal interface IActorReference - { - /// - /// Creates an that implements an actor interface for the actor using the - /// - /// method. - /// - /// Actor interface for the created to implement. - /// An actor proxy object that implements and TActorInterface. - object Bind(Type actorInterfaceType); - } -} + /// Actor interface for the created to implement. + /// An actor proxy object that implements and TActorInterface. + object Bind(Type actorInterfaceType); +} \ No newline at end of file diff --git a/src/Dapr.Actors/IDaprInteractor.cs b/src/Dapr.Actors/IDaprInteractor.cs index e0d91c44f..f857f90ff 100644 --- a/src/Dapr.Actors/IDaprInteractor.cs +++ b/src/Dapr.Actors/IDaprInteractor.cs @@ -13,108 +13,107 @@ using System.Net.Http; -namespace Dapr.Actors -{ - using System.IO; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Communication; +namespace Dapr.Actors; + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Communication; +/// +/// Interface for interacting with Dapr runtime. +/// +internal interface IDaprInteractor +{ /// - /// Interface for interacting with Dapr runtime. + /// Invokes an Actor method on Dapr without remoting. /// - internal interface IDaprInteractor - { - /// - /// Invokes an Actor method on Dapr without remoting. - /// - /// Type of actor. - /// ActorId. - /// Method name to invoke. - /// Serialized body. - /// Cancels the operation. - /// A task that represents the asynchronous operation. - Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default); + /// Type of actor. + /// ActorId. + /// Method name to invoke. + /// Serialized body. + /// Cancels the operation. + /// A task that represents the asynchronous operation. + Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default); - /// - /// Saves state batch to Dapr. - /// - /// Type of actor. - /// ActorId. - /// JSON data with state changes as per the Dapr spec for transaction state update. - /// Cancels the operation. - /// A task that represents the asynchronous operation. - Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, CancellationToken cancellationToken = default); + /// + /// Saves state batch to Dapr. + /// + /// Type of actor. + /// ActorId. + /// JSON data with state changes as per the Dapr spec for transaction state update. + /// Cancels the operation. + /// A task that represents the asynchronous operation. + Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, CancellationToken cancellationToken = default); - /// - /// Saves a state to Dapr. - /// - /// Type of actor. - /// ActorId. - /// Name of key to get value for. - /// Cancels the operation. - /// A task that represents the asynchronous operation. - Task> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default); + /// + /// Saves a state to Dapr. + /// + /// Type of actor. + /// ActorId. + /// Name of key to get value for. + /// Cancels the operation. + /// A task that represents the asynchronous operation. + Task> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default); - /// - /// Invokes Actor method. - /// - /// Serializers manager for remoting calls. - /// Actor Request Message. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - Task InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken = default); + /// + /// Invokes Actor method. + /// + /// Serializers manager for remoting calls. + /// Actor Request Message. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + Task InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken = default); - /// - /// Register a reminder. - /// - /// Type of actor. - /// ActorId. - /// Name of reminder to register. - /// JSON reminder data as per the Dapr spec. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, CancellationToken cancellationToken = default); + /// + /// Register a reminder. + /// + /// Type of actor. + /// ActorId. + /// Name of reminder to register. + /// JSON reminder data as per the Dapr spec. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, CancellationToken cancellationToken = default); - /// - /// Gets a reminder. - /// - /// Type of actor. - /// ActorId. - /// Name of reminder to unregister. - /// Cancels the operation. - /// A containing the response of the asynchronous HTTP operation. - Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default); + /// + /// Gets a reminder. + /// + /// Type of actor. + /// ActorId. + /// Name of reminder to unregister. + /// Cancels the operation. + /// A containing the response of the asynchronous HTTP operation. + Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default); - /// - /// Unregisters a reminder. - /// - /// Type of actor. - /// ActorId. - /// Name of reminder to unregister. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default); + /// + /// Unregisters a reminder. + /// + /// Type of actor. + /// ActorId. + /// Name of reminder to unregister. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default); - /// - /// Registers a timer. - /// - /// Type of actor. - /// ActorId. - /// Name of timer to register. - /// JSON reminder data as per the Dapr spec. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, CancellationToken cancellationToken = default); + /// + /// Registers a timer. + /// + /// Type of actor. + /// ActorId. + /// Name of timer to register. + /// JSON reminder data as per the Dapr spec. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, CancellationToken cancellationToken = default); - /// - /// Unegisters a timer. - /// - /// Type of actor. - /// ActorId. - /// Name of timer to register. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - Task UnregisterTimerAsync(string actorType, string actorId, string timerName, CancellationToken cancellationToken = default); - } -} + /// + /// Unegisters a timer. + /// + /// Type of actor. + /// ActorId. + /// Name of timer to register. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + Task UnregisterTimerAsync(string actorType, string actorId, string timerName, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/Dapr.Actors/JsonSerializerDefaults.cs b/src/Dapr.Actors/JsonSerializerDefaults.cs index 9dd222bd1..711e359a1 100644 --- a/src/Dapr.Actors/JsonSerializerDefaults.cs +++ b/src/Dapr.Actors/JsonSerializerDefaults.cs @@ -13,20 +13,19 @@ using System.Text.Json; -namespace Dapr.Actors +namespace Dapr.Actors; + +/// +/// A helper used to standardize the defaults +/// +internal static class JsonSerializerDefaults { /// - /// A helper used to standardize the defaults + /// defaults with Camel Casing and case insensitive properties /// - internal static class JsonSerializerDefaults + internal static JsonSerializerOptions Web => new JsonSerializerOptions { - /// - /// defaults with Camel Casing and case insensitive properties - /// - internal static JsonSerializerOptions Web => new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true - }; - } + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true + }; } \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/Actor.cs b/src/Dapr.Actors/Runtime/Actor.cs index ddfc266e9..bdd5ac521 100644 --- a/src/Dapr.Actors/Runtime/Actor.cs +++ b/src/Dapr.Actors/Runtime/Actor.cs @@ -11,572 +11,571 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +using System; +using System.Reflection; +using System.Threading.Tasks; +using Dapr.Actors.Client; +using Microsoft.Extensions.Logging; + +/// +/// Represents the base class for actors. +/// +/// +/// The base type for actors, that provides the common functionality +/// for actors that derive from . +/// The state is preserved across actor garbage collections and fail-overs. +/// +public abstract class Actor { - using System; - using System.Reflection; - using System.Threading.Tasks; - using Dapr.Actors.Client; - using Microsoft.Extensions.Logging; + private readonly string actorTypeName; /// - /// Represents the base class for actors. + /// The Logger /// - /// - /// The base type for actors, that provides the common functionality - /// for actors that derive from . - /// The state is preserved across actor garbage collections and fail-overs. - /// - public abstract class Actor + protected ILogger Logger { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The that will host this actor instance. + protected Actor(ActorHost host) { - private readonly string actorTypeName; - - /// - /// The Logger - /// - protected ILogger Logger { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The that will host this actor instance. - protected Actor(ActorHost host) - { - this.Host = host; - this.StateManager = new ActorStateManager(this); - this.actorTypeName = this.Host.ActorTypeInfo.ActorTypeName; - this.Logger = host.LoggerFactory.CreateLogger(this.GetType()); - } + this.Host = host; + this.StateManager = new ActorStateManager(this); + this.actorTypeName = this.Host.ActorTypeInfo.ActorTypeName; + this.Logger = host.LoggerFactory.CreateLogger(this.GetType()); + } - /// - /// Gets the identity of this actor. - /// - /// The for the actor. - public ActorId Id => Host.Id; - - /// - /// Gets the host of this actor within the actor runtime. - /// - /// The for the actor. - public ActorHost Host { get; } - - /// - /// Gets the used to create proxy client instances for communicating - /// with other actors. - /// - public IActorProxyFactory ProxyFactory => this.Host.ProxyFactory; - - /// - /// Gets the StateManager for the actor. - /// - public IActorStateManager StateManager { get; protected set; } - - internal async Task OnActivateInternalAsync() - { - await this.ResetStateAsync(); - await this.OnActivateAsync(); + /// + /// Gets the identity of this actor. + /// + /// The for the actor. + public ActorId Id => Host.Id; - this.Logger.LogDebug("Activated"); + /// + /// Gets the host of this actor within the actor runtime. + /// + /// The for the actor. + public ActorHost Host { get; } - // Save any state modifications done in user overridden Activate method. - await this.SaveStateAsync(); - } + /// + /// Gets the used to create proxy client instances for communicating + /// with other actors. + /// + public IActorProxyFactory ProxyFactory => this.Host.ProxyFactory; - internal async Task OnDeactivateInternalAsync() - { - this.Logger.LogDebug("Deactivating ..."); - await this.ResetStateAsync(); - await this.OnDeactivateAsync(); - this.Logger.LogDebug("Deactivated"); - } + /// + /// Gets the StateManager for the actor. + /// + public IActorStateManager StateManager { get; protected set; } - internal Task OnPreActorMethodAsyncInternal(ActorMethodContext actorMethodContext) - { - return this.OnPreActorMethodAsync(actorMethodContext); - } + internal async Task OnActivateInternalAsync() + { + await this.ResetStateAsync(); + await this.OnActivateAsync(); - internal async Task OnPostActorMethodAsyncInternal(ActorMethodContext actorMethodContext) - { - await this.OnPostActorMethodAsync(actorMethodContext); - await this.SaveStateAsync(); - } + this.Logger.LogDebug("Activated"); - internal async Task OnActorMethodFailedInternalAsync(ActorMethodContext actorMethodContext, Exception e) - { - await this.OnActorMethodFailedAsync(actorMethodContext, e); - // Exception has been thrown by user code, reset the state in state manager. - await this.ResetStateAsync(); - } + // Save any state modifications done in user overridden Activate method. + await this.SaveStateAsync(); + } - internal Task ResetStateAsync() - { - // Exception has been thrown by user code, reset the state in state manager. - return this.StateManager.ClearCacheAsync(); - } + internal async Task OnDeactivateInternalAsync() + { + this.Logger.LogDebug("Deactivating ..."); + await this.ResetStateAsync(); + await this.OnDeactivateAsync(); + this.Logger.LogDebug("Deactivated"); + } - /// - /// Saves all the state changes (add/update/remove) that were made since last call to - /// , - /// to the actor state provider associated with the actor. - /// - /// A task that represents the asynchronous save operation. - protected async Task SaveStateAsync() - { - await this.StateManager.SaveStateAsync(); - } + internal Task OnPreActorMethodAsyncInternal(ActorMethodContext actorMethodContext) + { + return this.OnPreActorMethodAsync(actorMethodContext); + } - /// - /// Override this method to initialize the members, initialize state or register timers. This method is called right after the actor is activated - /// and before any method call or reminders are dispatched on it. - /// - /// A Task that represents outstanding OnActivateAsync operation. - protected virtual Task OnActivateAsync() - { - return Task.CompletedTask; - } + internal async Task OnPostActorMethodAsyncInternal(ActorMethodContext actorMethodContext) + { + await this.OnPostActorMethodAsync(actorMethodContext); + await this.SaveStateAsync(); + } - /// - /// Override this method to release any resources. This method is called when actor is deactivated (garbage collected by Actor Runtime). - /// Actor operations like state changes should not be called from this method. - /// - /// A Task that represents outstanding OnDeactivateAsync operation. - protected virtual Task OnDeactivateAsync() - { - return Task.CompletedTask; - } + internal async Task OnActorMethodFailedInternalAsync(ActorMethodContext actorMethodContext, Exception e) + { + await this.OnActorMethodFailedAsync(actorMethodContext, e); + // Exception has been thrown by user code, reset the state in state manager. + await this.ResetStateAsync(); + } - /// - /// Override this method for performing any action prior to an actor method is invoked. - /// This method is invoked by actor runtime just before invoking an actor method. - /// - /// - /// An describing the method that will be invoked by actor runtime after this method finishes. - /// - /// - /// Returns a Task representing pre-actor-method operation. - /// - /// - /// This method is invoked by actor runtime prior to: - /// - /// Invoking an actor interface method when a client request comes. - /// Invoking a method when a reminder fires. - /// Invoking a timer callback when timer fires. - /// - /// - protected virtual Task OnPreActorMethodAsync(ActorMethodContext actorMethodContext) - { - return Task.CompletedTask; - } + internal Task ResetStateAsync() + { + // Exception has been thrown by user code, reset the state in state manager. + return this.StateManager.ClearCacheAsync(); + } - /// - /// Override this method for performing any action after an actor method has finished execution. - /// This method is invoked by actor runtime an actor method has finished execution. - /// - /// - /// An describing the method that was invoked by actor runtime prior to this method. - /// - /// - /// Returns a Task representing post-actor-method operation. - /// - /// /// - /// This method is invoked by actor runtime prior to: - /// - /// Invoking an actor interface method when a client request comes. - /// Invoking a method when a reminder fires. - /// Invoking a timer callback when timer fires. - /// - /// - protected virtual Task OnPostActorMethodAsync(ActorMethodContext actorMethodContext) - { - return Task.CompletedTask; - } + /// + /// Saves all the state changes (add/update/remove) that were made since last call to + /// , + /// to the actor state provider associated with the actor. + /// + /// A task that represents the asynchronous save operation. + protected async Task SaveStateAsync() + { + await this.StateManager.SaveStateAsync(); + } - /// - /// Override this method for performing any action when invoking actor method has thrown an exception. - /// This method is invoked by actor runtime when invoking actor method has thrown an exception. - /// - /// - /// An describing the method that was invoked by actor runtime prior to this method. - /// - /// Exception thrown by either actor method or by OnPreActorMethodAsync/OnPostActorMethodAsync overriden methods. - /// - /// Returns a Task representing post-actor-method operation. - /// - /// /// - /// This method is invoked by actor runtime prior to: - /// - /// Invoking an actor interface method when a client request comes. - /// Invoking a method when a reminder fires. - /// Invoking a timer callback when timer fires. - /// - /// - protected virtual Task OnActorMethodFailedAsync(ActorMethodContext actorMethodContext, Exception e) - { - return Task.CompletedTask; - } + /// + /// Override this method to initialize the members, initialize state or register timers. This method is called right after the actor is activated + /// and before any method call or reminders are dispatched on it. + /// + /// A Task that represents outstanding OnActivateAsync operation. + protected virtual Task OnActivateAsync() + { + return Task.CompletedTask; + } + + /// + /// Override this method to release any resources. This method is called when actor is deactivated (garbage collected by Actor Runtime). + /// Actor operations like state changes should not be called from this method. + /// + /// A Task that represents outstanding OnDeactivateAsync operation. + protected virtual Task OnDeactivateAsync() + { + return Task.CompletedTask; + } + + /// + /// Override this method for performing any action prior to an actor method is invoked. + /// This method is invoked by actor runtime just before invoking an actor method. + /// + /// + /// An describing the method that will be invoked by actor runtime after this method finishes. + /// + /// + /// Returns a Task representing pre-actor-method operation. + /// + /// + /// This method is invoked by actor runtime prior to: + /// + /// Invoking an actor interface method when a client request comes. + /// Invoking a method when a reminder fires. + /// Invoking a timer callback when timer fires. + /// + /// + protected virtual Task OnPreActorMethodAsync(ActorMethodContext actorMethodContext) + { + return Task.CompletedTask; + } + + /// + /// Override this method for performing any action after an actor method has finished execution. + /// This method is invoked by actor runtime an actor method has finished execution. + /// + /// + /// An describing the method that was invoked by actor runtime prior to this method. + /// + /// + /// Returns a Task representing post-actor-method operation. + /// + /// /// + /// This method is invoked by actor runtime prior to: + /// + /// Invoking an actor interface method when a client request comes. + /// Invoking a method when a reminder fires. + /// Invoking a timer callback when timer fires. + /// + /// + protected virtual Task OnPostActorMethodAsync(ActorMethodContext actorMethodContext) + { + return Task.CompletedTask; + } + + /// + /// Override this method for performing any action when invoking actor method has thrown an exception. + /// This method is invoked by actor runtime when invoking actor method has thrown an exception. + /// + /// + /// An describing the method that was invoked by actor runtime prior to this method. + /// + /// Exception thrown by either actor method or by OnPreActorMethodAsync/OnPostActorMethodAsync overriden methods. + /// + /// Returns a Task representing post-actor-method operation. + /// + /// /// + /// This method is invoked by actor runtime prior to: + /// + /// Invoking an actor interface method when a client request comes. + /// Invoking a method when a reminder fires. + /// Invoking a timer callback when timer fires. + /// + /// + protected virtual Task OnActorMethodFailedAsync(ActorMethodContext actorMethodContext, Exception e) + { + return Task.CompletedTask; + } - /// - /// Registers a reminder with the actor. - /// - /// The name of the reminder to register. The name must be unique per actor. - /// User state passed to the reminder invocation. - /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. - /// - /// - /// The time interval between reminder invocations after the first invocation. Specify negative one (-1) milliseconds to disable periodic invocation. - /// - /// - /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync />. - /// - /// - /// - /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. - /// - /// - protected async Task RegisterReminderAsync( - string reminderName, - byte[] state, - TimeSpan dueTime, - TimeSpan period) + /// + /// Registers a reminder with the actor. + /// + /// The name of the reminder to register. The name must be unique per actor. + /// User state passed to the reminder invocation. + /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. + /// + /// + /// The time interval between reminder invocations after the first invocation. Specify negative one (-1) milliseconds to disable periodic invocation. + /// + /// + /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync />. + /// + /// + /// + /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. + /// + /// + protected async Task RegisterReminderAsync( + string reminderName, + byte[] state, + TimeSpan dueTime, + TimeSpan period) + { + return await RegisterReminderAsync(new ActorReminderOptions { - return await RegisterReminderAsync(new ActorReminderOptions - { - ActorTypeName = this.actorTypeName, - Id = this.Id, - ReminderName = reminderName, - State = state, - DueTime = dueTime, - Period = period, - Ttl = null - }); - } + ActorTypeName = this.actorTypeName, + Id = this.Id, + ReminderName = reminderName, + State = state, + DueTime = dueTime, + Period = period, + Ttl = null + }); + } - /// - /// Registers a reminder with the actor. - /// - /// The name of the reminder to register. The name must be unique per actor. - /// User state passed to the reminder invocation. - /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. - /// - /// - /// The time interval between reminder invocations after the first invocation. Specify negative one (-1) milliseconds to disable periodic invocation. - /// - /// The time interval after which the reminder will expire. - /// - /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync />. - /// - /// - /// - /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. - /// - /// - protected async Task RegisterReminderAsync( - string reminderName, - byte[] state, - TimeSpan dueTime, - TimeSpan period, - TimeSpan ttl) + /// + /// Registers a reminder with the actor. + /// + /// The name of the reminder to register. The name must be unique per actor. + /// User state passed to the reminder invocation. + /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. + /// + /// + /// The time interval between reminder invocations after the first invocation. Specify negative one (-1) milliseconds to disable periodic invocation. + /// + /// The time interval after which the reminder will expire. + /// + /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync />. + /// + /// + /// + /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. + /// + /// + protected async Task RegisterReminderAsync( + string reminderName, + byte[] state, + TimeSpan dueTime, + TimeSpan period, + TimeSpan ttl) + { + return await RegisterReminderAsync(new ActorReminderOptions { - return await RegisterReminderAsync(new ActorReminderOptions - { - ActorTypeName = this.actorTypeName, - Id = this.Id, - ReminderName = reminderName, - State = state, - DueTime = dueTime, - Period = period, - Ttl = ttl - }); - } + ActorTypeName = this.actorTypeName, + Id = this.Id, + ReminderName = reminderName, + State = state, + DueTime = dueTime, + Period = period, + Ttl = ttl + }); + } - /// - /// Registers a reminder with the actor. - /// - /// The name of the reminder to register. The name must be unique per actor. - /// User state passed to the reminder invocation. - /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. - /// - /// The time interval between reminder invocations after the first invocation. - /// - /// The number of repetitions for which the reminder should be invoked. - /// The time interval after which the reminder will expire. - /// - /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync. - /// - /// - /// - /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. - /// - /// - protected async Task RegisterReminderAsync( - string reminderName, - byte[] state, - TimeSpan dueTime, - TimeSpan period, - int repetitions, - TimeSpan ttl) + /// + /// Registers a reminder with the actor. + /// + /// The name of the reminder to register. The name must be unique per actor. + /// User state passed to the reminder invocation. + /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. + /// + /// The time interval between reminder invocations after the first invocation. + /// + /// The number of repetitions for which the reminder should be invoked. + /// The time interval after which the reminder will expire. + /// + /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync. + /// + /// + /// + /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. + /// + /// + protected async Task RegisterReminderAsync( + string reminderName, + byte[] state, + TimeSpan dueTime, + TimeSpan period, + int repetitions, + TimeSpan ttl) + { + return await RegisterReminderAsync(new ActorReminderOptions { - return await RegisterReminderAsync(new ActorReminderOptions - { - ActorTypeName = this.actorTypeName, - Id = this.Id, - ReminderName = reminderName, - State = state, - DueTime = dueTime, - Period = period, - Repetitions = repetitions, - Ttl = ttl - }); - } + ActorTypeName = this.actorTypeName, + Id = this.Id, + ReminderName = reminderName, + State = state, + DueTime = dueTime, + Period = period, + Repetitions = repetitions, + Ttl = ttl + }); + } - /// - /// Registers a reminder with the actor. - /// - /// The name of the reminder to register. The name must be unique per actor. - /// User state passed to the reminder invocation. - /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. - /// - /// The time interval between reminder invocations after the first invocation. - /// - /// The number of repetitions for which the reminder should be invoked. - /// - /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync. - /// - /// - /// - /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. - /// - /// - protected async Task RegisterReminderAsync( - string reminderName, - byte[] state, - TimeSpan dueTime, - TimeSpan period, - int repetitions) + /// + /// Registers a reminder with the actor. + /// + /// The name of the reminder to register. The name must be unique per actor. + /// User state passed to the reminder invocation. + /// The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration. + /// + /// The time interval between reminder invocations after the first invocation. + /// + /// The number of repetitions for which the reminder should be invoked. + /// + /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync. + /// + /// + /// + /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. + /// + /// + protected async Task RegisterReminderAsync( + string reminderName, + byte[] state, + TimeSpan dueTime, + TimeSpan period, + int repetitions) + { + return await RegisterReminderAsync(new ActorReminderOptions { - return await RegisterReminderAsync(new ActorReminderOptions - { - ActorTypeName = this.actorTypeName, - Id = this.Id, - ReminderName = reminderName, - State = state, - DueTime = dueTime, - Period = period, - Repetitions = repetitions - }); - } + ActorTypeName = this.actorTypeName, + Id = this.Id, + ReminderName = reminderName, + State = state, + DueTime = dueTime, + Period = period, + Repetitions = repetitions + }); + } - /// - /// Registers a reminder with the actor. - /// - /// A containing the various settings for an . - /// - /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync />. - /// - /// - /// - /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. - /// - /// - internal async Task RegisterReminderAsync(ActorReminderOptions options) - { - var reminder = new ActorReminder(options); - await this.Host.TimerManager.RegisterReminderAsync(reminder); - return reminder; - } + /// + /// Registers a reminder with the actor. + /// + /// A containing the various settings for an . + /// + /// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync />. + /// + /// + /// + /// The class deriving from must implement to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by . Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks. + /// + /// + internal async Task RegisterReminderAsync(ActorReminderOptions options) + { + var reminder = new ActorReminder(options); + await this.Host.TimerManager.RegisterReminderAsync(reminder); + return reminder; + } - /// - /// Gets a reminder previously registered using . - /// - /// The name of the reminder to get. - /// - /// Returns a task that represents the asynchronous get operation. The result of the task contains the reminder if it exists, otherwise null. - /// - protected async Task GetReminderAsync(string reminderName) - { - return await this.Host.TimerManager.GetReminderAsync(new ActorReminderToken(this.actorTypeName, this.Id, reminderName)); - } + /// + /// Gets a reminder previously registered using . + /// + /// The name of the reminder to get. + /// + /// Returns a task that represents the asynchronous get operation. The result of the task contains the reminder if it exists, otherwise null. + /// + protected async Task GetReminderAsync(string reminderName) + { + return await this.Host.TimerManager.GetReminderAsync(new ActorReminderToken(this.actorTypeName, this.Id, reminderName)); + } - /// - /// Unregisters a reminder previously registered using . - /// - /// The actor reminder to unregister. - /// - /// Returns a task that represents the asynchronous unregistration operation. - /// - protected Task UnregisterReminderAsync(IActorReminder reminder) - { - var token = reminder is ActorReminderToken ? (ActorReminderToken)reminder : new ActorReminderToken(this.actorTypeName, this.Id, reminder.Name); - return this.Host.TimerManager.UnregisterReminderAsync(token); - } + /// + /// Unregisters a reminder previously registered using . + /// + /// The actor reminder to unregister. + /// + /// Returns a task that represents the asynchronous unregistration operation. + /// + protected Task UnregisterReminderAsync(IActorReminder reminder) + { + var token = reminder is ActorReminderToken ? (ActorReminderToken)reminder : new ActorReminderToken(this.actorTypeName, this.Id, reminder.Name); + return this.Host.TimerManager.UnregisterReminderAsync(token); + } + + /// + /// Unregisters a reminder previously registered using . + /// + /// The actor reminder name to unregister. + /// + /// Returns a task that represents the asynchronous unregistration operation. + /// + protected Task UnregisterReminderAsync(string reminderName) + { + var token = new ActorReminderToken(this.actorTypeName, this.Id, reminderName); + return this.Host.TimerManager.UnregisterReminderAsync(token); + } - /// - /// Unregisters a reminder previously registered using . - /// - /// The actor reminder name to unregister. - /// - /// Returns a task that represents the asynchronous unregistration operation. - /// - protected Task UnregisterReminderAsync(string reminderName) + /// + /// Registers a Timer for the actor. A timer name is autogenerated by the runtime to keep track of it. + /// + /// Timer Name. If a timer name is not provided, a timer is autogenerated. + /// + /// The name of the method to be called when the timer fires. + /// It has one parameter: the state object passed to RegisterTimer. + /// It returns a representing the asynchronous operation. + /// + /// An object containing information to be used by the callback method, or null. + /// The amount of time to delay before the async callback is first invoked. + /// Specify negative one (-1) milliseconds to prevent the timer from starting. + /// Specify zero (0) to start the timer immediately. + /// + /// + /// The time interval between invocations of the async callback. + /// Specify negative one (-1) milliseconds to disable periodic signaling. + /// Returns IActorTimer object. + public async Task RegisterTimerAsync( + string timerName, + string callback, + byte[] callbackParams, + TimeSpan dueTime, + TimeSpan period) + { + return await RegisterTimerAsync(new ActorTimerOptions { - var token = new ActorReminderToken(this.actorTypeName, this.Id, reminderName); - return this.Host.TimerManager.UnregisterReminderAsync(token); - } + ActorTypeName = this.actorTypeName, + Id = this.Id, + TimerName = timerName, + TimerCallback = callback, + Data = callbackParams, + DueTime = dueTime, + Period = period, + Ttl = null + }); + } - /// - /// Registers a Timer for the actor. A timer name is autogenerated by the runtime to keep track of it. - /// - /// Timer Name. If a timer name is not provided, a timer is autogenerated. - /// - /// The name of the method to be called when the timer fires. - /// It has one parameter: the state object passed to RegisterTimer. - /// It returns a representing the asynchronous operation. - /// - /// An object containing information to be used by the callback method, or null. - /// The amount of time to delay before the async callback is first invoked. - /// Specify negative one (-1) milliseconds to prevent the timer from starting. - /// Specify zero (0) to start the timer immediately. - /// - /// - /// The time interval between invocations of the async callback. - /// Specify negative one (-1) milliseconds to disable periodic signaling. - /// Returns IActorTimer object. - public async Task RegisterTimerAsync( - string timerName, - string callback, - byte[] callbackParams, - TimeSpan dueTime, - TimeSpan period) + /// + /// Registers a Timer for the actor. A timer name is autogenerated by the runtime to keep track of it. + /// + /// Timer Name. If a timer name is not provided, a timer is autogenerated. + /// + /// The name of the method to be called when the timer fires. + /// It has one parameter: the state object passed to RegisterTimer. + /// It returns a representing the asynchronous operation. + /// + /// An object containing information to be used by the callback method, or null. + /// The amount of time to delay before the async callback is first invoked. + /// Specify negative one (-1) milliseconds to prevent the timer from starting. + /// Specify zero (0) to start the timer immediately. + /// + /// + /// The time interval between invocations of the async callback. + /// Specify negative one (-1) milliseconds to disable periodic signaling. + /// The time interval after which a Timer will expire. + /// Returns IActorTimer object. + public async Task RegisterTimerAsync( + string timerName, + string callback, + byte[] callbackParams, + TimeSpan dueTime, + TimeSpan period, + TimeSpan ttl) + { + return await RegisterTimerAsync(new ActorTimerOptions { - return await RegisterTimerAsync(new ActorTimerOptions - { - ActorTypeName = this.actorTypeName, - Id = this.Id, - TimerName = timerName, - TimerCallback = callback, - Data = callbackParams, - DueTime = dueTime, - Period = period, - Ttl = null - }); - } + ActorTypeName = this.actorTypeName, + Id = this.Id, + TimerName = timerName, + TimerCallback = callback, + Data = callbackParams, + DueTime = dueTime, + Period = period, + Ttl = ttl + }); + } - /// - /// Registers a Timer for the actor. A timer name is autogenerated by the runtime to keep track of it. - /// - /// Timer Name. If a timer name is not provided, a timer is autogenerated. - /// - /// The name of the method to be called when the timer fires. - /// It has one parameter: the state object passed to RegisterTimer. - /// It returns a representing the asynchronous operation. - /// - /// An object containing information to be used by the callback method, or null. - /// The amount of time to delay before the async callback is first invoked. - /// Specify negative one (-1) milliseconds to prevent the timer from starting. - /// Specify zero (0) to start the timer immediately. - /// - /// - /// The time interval between invocations of the async callback. - /// Specify negative one (-1) milliseconds to disable periodic signaling. - /// The time interval after which a Timer will expire. - /// Returns IActorTimer object. - public async Task RegisterTimerAsync( - string timerName, - string callback, - byte[] callbackParams, - TimeSpan dueTime, - TimeSpan period, - TimeSpan ttl) + internal async Task RegisterTimerAsync(ActorTimerOptions options) + { + // Validate that the timer callback specified meets all the required criteria for a valid callback method + this.ValidateTimerCallback(this.Host, options.TimerCallback); + + // create a timer name to register with Dapr runtime. + if (string.IsNullOrEmpty(options.TimerName)) { - return await RegisterTimerAsync(new ActorTimerOptions - { - ActorTypeName = this.actorTypeName, - Id = this.Id, - TimerName = timerName, - TimerCallback = callback, - Data = callbackParams, - DueTime = dueTime, - Period = period, - Ttl = ttl - }); + options.TimerName = $"{this.Id}_Timer_{Guid.NewGuid()}"; } - internal async Task RegisterTimerAsync(ActorTimerOptions options) + var actorTimer = new ActorTimer(options); + await this.Host.TimerManager.RegisterTimerAsync(actorTimer); + return actorTimer; + } + + /// + /// Unregisters a Timer previously set on this actor. + /// + /// An IActorTimer representing timer that needs to be unregistered. + /// Task representing the Unregister timer operation. + protected async Task UnregisterTimerAsync(ActorTimer timer) + { + await this.Host.TimerManager.UnregisterTimerAsync(timer); + } + + /// + /// Unregisters a Timer previously set on this actor. + /// + /// Name of timer to unregister. + /// Task representing the Unregister timer operation. + protected async Task UnregisterTimerAsync(string timerName) + { + var token = new ActorTimerToken(this.actorTypeName, this.Id, timerName); + await this.Host.TimerManager.UnregisterTimerAsync(token); + } + + internal MethodInfo GetMethodInfoUsingReflection(Type actorType, string callback) + { + return actorType.GetMethod(callback, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); + } + + internal void ValidateTimerCallback(ActorHost host, string callback) + { + var actorTypeName = host.ActorTypeInfo.ActorTypeName; + var actorType = host.ActorTypeInfo.ImplementationType; + + MethodInfo methodInfo; + try { - // Validate that the timer callback specified meets all the required criteria for a valid callback method - this.ValidateTimerCallback(this.Host, options.TimerCallback); - - // create a timer name to register with Dapr runtime. - if (string.IsNullOrEmpty(options.TimerName)) - { - options.TimerName = $"{this.Id}_Timer_{Guid.NewGuid()}"; - } - - var actorTimer = new ActorTimer(options); - await this.Host.TimerManager.RegisterTimerAsync(actorTimer); - return actorTimer; + methodInfo = this.GetMethodInfoUsingReflection(actorType, callback); } - - /// - /// Unregisters a Timer previously set on this actor. - /// - /// An IActorTimer representing timer that needs to be unregistered. - /// Task representing the Unregister timer operation. - protected async Task UnregisterTimerAsync(ActorTimer timer) + catch (AmbiguousMatchException) { - await this.Host.TimerManager.UnregisterTimerAsync(timer); + // GetMethod will throw an AmbiguousMatchException if more than one methods are found with the same name + throw new ArgumentException($"Timer callback method: {callback} cannot be overloaded."); } - /// - /// Unregisters a Timer previously set on this actor. - /// - /// Name of timer to unregister. - /// Task representing the Unregister timer operation. - protected async Task UnregisterTimerAsync(string timerName) + // Check if the method exists + if (methodInfo == null) { - var token = new ActorTimerToken(this.actorTypeName, this.Id, timerName); - await this.Host.TimerManager.UnregisterTimerAsync(token); + throw new ArgumentException($"Timer callback method: {callback} does not exist in the Actor class: {actorTypeName}"); } - internal MethodInfo GetMethodInfoUsingReflection(Type actorType, string callback) + // The timer callback can accept only 0 or 1 parameters + var parameters = methodInfo.GetParameters(); + if (parameters.Length > 1) { - return actorType.GetMethod(callback, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); + throw new ArgumentException("Timer callback can accept only zero or one parameters"); } - internal void ValidateTimerCallback(ActorHost host, string callback) + // The timer callback should return only Task return type + if (methodInfo.ReturnType != typeof(Task)) { - var actorTypeName = host.ActorTypeInfo.ActorTypeName; - var actorType = host.ActorTypeInfo.ImplementationType; - - MethodInfo methodInfo; - try - { - methodInfo = this.GetMethodInfoUsingReflection(actorType, callback); - } - catch (AmbiguousMatchException) - { - // GetMethod will throw an AmbiguousMatchException if more than one methods are found with the same name - throw new ArgumentException($"Timer callback method: {callback} cannot be overloaded."); - } - - // Check if the method exists - if (methodInfo == null) - { - throw new ArgumentException($"Timer callback method: {callback} does not exist in the Actor class: {actorTypeName}"); - } - - // The timer callback can accept only 0 or 1 parameters - var parameters = methodInfo.GetParameters(); - if (parameters.Length > 1) - { - throw new ArgumentException("Timer callback can accept only zero or one parameters"); - } - - // The timer callback should return only Task return type - if (methodInfo.ReturnType != typeof(Task)) - { - throw new ArgumentException("Timer callback can only return type Task"); - } + throw new ArgumentException("Timer callback can only return type Task"); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorActivator.cs b/src/Dapr.Actors/Runtime/ActorActivator.cs index 1d7708ce4..b0fa29ae0 100644 --- a/src/Dapr.Actors/Runtime/ActorActivator.cs +++ b/src/Dapr.Actors/Runtime/ActorActivator.cs @@ -13,40 +13,39 @@ using System.Threading.Tasks; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// An abstraction for implementing the creation and deletion of actor instances. +/// +public abstract class ActorActivator { /// - /// An abstraction for implementing the creation and deletion of actor instances. + /// Creates the actor instance and returns it inside an instance of . /// - public abstract class ActorActivator - { - /// - /// Creates the actor instance and returns it inside an instance of . - /// - /// The actor host specifying information needed for the creation of the actor. - /// - /// Asynchronously returns n instance of . The - /// instance will be provided to when the actor is ready for deletion. - /// - /// - /// Implementations should not interact with lifecycle callback methods on the type. - /// These methods will be called by the runtime. - /// - public abstract Task CreateAsync(ActorHost host); + /// The actor host specifying information needed for the creation of the actor. + /// + /// Asynchronously returns n instance of . The + /// instance will be provided to when the actor is ready for deletion. + /// + /// + /// Implementations should not interact with lifecycle callback methods on the type. + /// These methods will be called by the runtime. + /// + public abstract Task CreateAsync(ActorHost host); - /// - /// Deletes the actor instance and cleans up all associated resources. - /// - /// - /// The instance that was created during creation of the actor. - /// - /// - /// A task that represents the asynchronous completion of the operation. - /// - /// - /// Implementations should not interact with lifecycle callback methods on the type. - /// These methods will be called by the runtime. - /// - public abstract Task DeleteAsync(ActorActivatorState state); - } -} + /// + /// Deletes the actor instance and cleans up all associated resources. + /// + /// + /// The instance that was created during creation of the actor. + /// + /// + /// A task that represents the asynchronous completion of the operation. + /// + /// + /// Implementations should not interact with lifecycle callback methods on the type. + /// These methods will be called by the runtime. + /// + public abstract Task DeleteAsync(ActorActivatorState state); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorActivatorFactory.cs b/src/Dapr.Actors/Runtime/ActorActivatorFactory.cs index 00394a5b2..e25d12a1d 100644 --- a/src/Dapr.Actors/Runtime/ActorActivatorFactory.cs +++ b/src/Dapr.Actors/Runtime/ActorActivatorFactory.cs @@ -11,19 +11,18 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// An abstraction used to construct an for a given +/// . +/// +public abstract class ActorActivatorFactory { /// - /// An abstraction used to construct an for a given - /// . + /// Creates the for the provided . /// - public abstract class ActorActivatorFactory - { - /// - /// Creates the for the provided . - /// - /// The . - /// An . - public abstract ActorActivator CreateActivator(ActorTypeInformation type); - } -} + /// The . + /// An . + public abstract ActorActivator CreateActivator(ActorTypeInformation type); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorActivatorState.cs b/src/Dapr.Actors/Runtime/ActorActivatorState.cs index e9e33e0a7..4021657d9 100644 --- a/src/Dapr.Actors/Runtime/ActorActivatorState.cs +++ b/src/Dapr.Actors/Runtime/ActorActivatorState.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// A state object created by an implementation of . Implementations +/// can return a subclass of to associate additional data +/// with an Actor instance. +/// +public class ActorActivatorState { /// - /// A state object created by an implementation of . Implementations - /// can return a subclass of to associate additional data - /// with an Actor instance. + /// Initializes a new instance of . /// - public class ActorActivatorState + /// The instance. + public ActorActivatorState(Actor actor) { - /// - /// Initializes a new instance of . - /// - /// The instance. - public ActorActivatorState(Actor actor) - { - Actor = actor; - } - - /// - /// Gets the instance. - /// - public Actor Actor { get; } + Actor = actor; } -} + + /// + /// Gets the instance. + /// + public Actor Actor { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorAttribute.cs b/src/Dapr.Actors/Runtime/ActorAttribute.cs index f4869dbee..4cf6b539b 100644 --- a/src/Dapr.Actors/Runtime/ActorAttribute.cs +++ b/src/Dapr.Actors/Runtime/ActorAttribute.cs @@ -11,22 +11,21 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime -{ - using System; +namespace Dapr.Actors.Runtime; + +using System; +/// +/// Contains optional properties related to an actor implementation. +/// +/// Intended to be attached to actor implementation types (i.e.those derived from ). +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] +public sealed class ActorAttribute : Attribute +{ /// - /// Contains optional properties related to an actor implementation. + /// Gets or sets the name of the actor type represented by the actor. /// - /// Intended to be attached to actor implementation types (i.e.those derived from ). - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ActorAttribute : Attribute - { - /// - /// Gets or sets the name of the actor type represented by the actor. - /// - /// The name of the actor type represented by the actor. - /// If set, this value will override the default actor type name derived from the actor implementation type. - public string TypeName { get; set; } - } + /// The name of the actor type represented by the actor. + /// If set, this value will override the default actor type name derived from the actor implementation type. + public string TypeName { get; set; } } \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorCallType.cs b/src/Dapr.Actors/Runtime/ActorCallType.cs index 0b961d495..c128f695d 100644 --- a/src/Dapr.Actors/Runtime/ActorCallType.cs +++ b/src/Dapr.Actors/Runtime/ActorCallType.cs @@ -11,30 +11,29 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents the call-type associated with the method invoked by actor runtime. +/// +/// +/// This is provided as part of which is passed as argument to +/// and . +/// +public enum ActorCallType { /// - /// Represents the call-type associated with the method invoked by actor runtime. + /// Specifies that the method invoked is an actor interface method for a given client request. /// - /// - /// This is provided as part of which is passed as argument to - /// and . - /// - public enum ActorCallType - { - /// - /// Specifies that the method invoked is an actor interface method for a given client request. - /// - ActorInterfaceMethod = 0, + ActorInterfaceMethod = 0, - /// - /// Specifies that the method invoked is a timer callback method. - /// - TimerMethod = 1, + /// + /// Specifies that the method invoked is a timer callback method. + /// + TimerMethod = 1, - /// - /// Specifies that the method is when a reminder fires. - /// - ReminderMethod = 2, - } -} + /// + /// Specifies that the method is when a reminder fires. + /// + ReminderMethod = 2, +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorHost.cs b/src/Dapr.Actors/Runtime/ActorHost.cs index dca0edb63..fab64e67b 100644 --- a/src/Dapr.Actors/Runtime/ActorHost.cs +++ b/src/Dapr.Actors/Runtime/ActorHost.cs @@ -11,156 +11,155 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +using System; +using System.Text.Json; +using Dapr.Actors.Client; +using Microsoft.Extensions.Logging; + +/// +/// Represents a host for an actor type within the actor runtime. +/// +public sealed class ActorHost { - using System; - using System.Text.Json; - using Dapr.Actors.Client; - using Microsoft.Extensions.Logging; + /// + /// Creates an instance of for unit testing an actor instance. + /// + /// The for configuring the host. + /// The actor type. + /// An instance. + public static ActorHost CreateForTest(ActorTestOptions options = null) + where TActor : Actor + { + return CreateForTest(typeof(TActor), actorTypeName: null, options); + } /// - /// Represents a host for an actor type within the actor runtime. + /// Creates an instance of for unit testing an actor instance. /// - public sealed class ActorHost + /// The name of the actor type represented by the actor. + /// The for configuring the host. + /// The actor type. + /// An instance. + public static ActorHost CreateForTest(string actorTypeName, ActorTestOptions options = null) + where TActor : Actor { - /// - /// Creates an instance of for unit testing an actor instance. - /// - /// The for configuring the host. - /// The actor type. - /// An instance. - public static ActorHost CreateForTest(ActorTestOptions options = null) - where TActor : Actor - { - return CreateForTest(typeof(TActor), actorTypeName: null, options); - } + return CreateForTest(typeof(TActor), actorTypeName, options); + } - /// - /// Creates an instance of for unit testing an actor instance. - /// - /// The name of the actor type represented by the actor. - /// The for configuring the host. - /// The actor type. - /// An instance. - public static ActorHost CreateForTest(string actorTypeName, ActorTestOptions options = null) - where TActor : Actor - { - return CreateForTest(typeof(TActor), actorTypeName, options); - } + /// + /// Creates an instance of for unit testing an actor instance. + /// + /// The actor type. + /// The for configuring the host. + /// An instance. + public static ActorHost CreateForTest(Type actorType, ActorTestOptions options = null) + { + return CreateForTest(actorType, actorTypeName: null, options); + } - /// - /// Creates an instance of for unit testing an actor instance. - /// - /// The actor type. - /// The for configuring the host. - /// An instance. - public static ActorHost CreateForTest(Type actorType, ActorTestOptions options = null) + /// + /// Creates an instance of for unit testing an actor instance. + /// + /// The actor type. + /// The name of the actor type represented by the actor. + /// The for configuring the host. + /// An instance. + public static ActorHost CreateForTest(Type actorType, string actorTypeName, ActorTestOptions options = null) + { + if (actorType == null) { - return CreateForTest(actorType, actorTypeName: null, options); + throw new ArgumentNullException(nameof(actorType)); } - /// - /// Creates an instance of for unit testing an actor instance. - /// - /// The actor type. - /// The name of the actor type represented by the actor. - /// The for configuring the host. - /// An instance. - public static ActorHost CreateForTest(Type actorType, string actorTypeName, ActorTestOptions options = null) - { - if (actorType == null) - { - throw new ArgumentNullException(nameof(actorType)); - } - - options ??= new ActorTestOptions(); - - return new ActorHost( - ActorTypeInformation.Get(actorType, actorTypeName), - options.ActorId, - options.JsonSerializerOptions, - options.LoggerFactory, - options.ProxyFactory, - options.TimerManager); - } + options ??= new ActorTestOptions(); - /// - /// Initializes a new instance of the class. - /// - /// The type information of the Actor. - /// The id of the Actor instance. - /// The to use for actor state persistence and message deserialization. - /// The logger factory. - /// The . - [Obsolete("Application code should not call this method. Use CreateForTest for unit testing.")] - public ActorHost( - ActorTypeInformation actorTypeInfo, - ActorId id, - JsonSerializerOptions jsonSerializerOptions, - ILoggerFactory loggerFactory, - IActorProxyFactory proxyFactory) - { - this.ActorTypeInfo = actorTypeInfo; - this.Id = id; - this.LoggerFactory = loggerFactory; - this.ProxyFactory = proxyFactory; - } + return new ActorHost( + ActorTypeInformation.Get(actorType, actorTypeName), + options.ActorId, + options.JsonSerializerOptions, + options.LoggerFactory, + options.ProxyFactory, + options.TimerManager); + } - /// - /// Initializes a new instance of the class. - /// - /// The type information of the Actor. - /// The id of the Actor instance. - /// The to use for actor state persistence and message deserialization. - /// The logger factory. - /// The . - /// The . - internal ActorHost( - ActorTypeInformation actorTypeInfo, - ActorId id, - JsonSerializerOptions jsonSerializerOptions, - ILoggerFactory loggerFactory, - IActorProxyFactory proxyFactory, - ActorTimerManager timerManager) - { - this.ActorTypeInfo = actorTypeInfo; - this.Id = id; - this.JsonSerializerOptions = jsonSerializerOptions; - this.LoggerFactory = loggerFactory; - this.ProxyFactory = proxyFactory; - this.TimerManager = timerManager; - } + /// + /// Initializes a new instance of the class. + /// + /// The type information of the Actor. + /// The id of the Actor instance. + /// The to use for actor state persistence and message deserialization. + /// The logger factory. + /// The . + [Obsolete("Application code should not call this method. Use CreateForTest for unit testing.")] + public ActorHost( + ActorTypeInformation actorTypeInfo, + ActorId id, + JsonSerializerOptions jsonSerializerOptions, + ILoggerFactory loggerFactory, + IActorProxyFactory proxyFactory) + { + this.ActorTypeInfo = actorTypeInfo; + this.Id = id; + this.LoggerFactory = loggerFactory; + this.ProxyFactory = proxyFactory; + } - /// - /// Gets the ActorTypeInformation for actor service. - /// - public ActorTypeInformation ActorTypeInfo { get; } - - /// - /// Gets the . - /// - public ActorId Id { get; } - - /// - /// Gets the LoggerFactory for actor service - /// - public ILoggerFactory LoggerFactory { get; } - - /// - /// Gets the . - /// - public JsonSerializerOptions JsonSerializerOptions { get; } - - /// - /// Gets the . - /// - public IActorProxyFactory ProxyFactory { get; } - - /// - /// Gets the . - /// - public ActorTimerManager TimerManager { get; } - - internal DaprStateProvider StateProvider { get; set; } + /// + /// Initializes a new instance of the class. + /// + /// The type information of the Actor. + /// The id of the Actor instance. + /// The to use for actor state persistence and message deserialization. + /// The logger factory. + /// The . + /// The . + internal ActorHost( + ActorTypeInformation actorTypeInfo, + ActorId id, + JsonSerializerOptions jsonSerializerOptions, + ILoggerFactory loggerFactory, + IActorProxyFactory proxyFactory, + ActorTimerManager timerManager) + { + this.ActorTypeInfo = actorTypeInfo; + this.Id = id; + this.JsonSerializerOptions = jsonSerializerOptions; + this.LoggerFactory = loggerFactory; + this.ProxyFactory = proxyFactory; + this.TimerManager = timerManager; } -} + + /// + /// Gets the ActorTypeInformation for actor service. + /// + public ActorTypeInformation ActorTypeInfo { get; } + + /// + /// Gets the . + /// + public ActorId Id { get; } + + /// + /// Gets the LoggerFactory for actor service + /// + public ILoggerFactory LoggerFactory { get; } + + /// + /// Gets the . + /// + public JsonSerializerOptions JsonSerializerOptions { get; } + + /// + /// Gets the . + /// + public IActorProxyFactory ProxyFactory { get; } + + /// + /// Gets the . + /// + public ActorTimerManager TimerManager { get; } + + internal DaprStateProvider StateProvider { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorManager.cs b/src/Dapr.Actors/Runtime/ActorManager.cs index c78126ccd..e60cd455a 100644 --- a/src/Dapr.Actors/Runtime/ActorManager.cs +++ b/src/Dapr.Actors/Runtime/ActorManager.cs @@ -11,391 +11,390 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.Actors.Client; +using Dapr.Actors.Communication; +using Microsoft.Extensions.Logging; + +// The ActorManager serves as a cache for a variety of different concerns related to an Actor type +// as well as the runtime managment for Actor instances of that type. +internal sealed class ActorManager { - using System; - using System.Collections.Concurrent; - using System.IO; - using System.Text; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.Actors.Client; - using Dapr.Actors.Communication; - using Microsoft.Extensions.Logging; - - // The ActorManager serves as a cache for a variety of different concerns related to an Actor type - // as well as the runtime managment for Actor instances of that type. - internal sealed class ActorManager + private const string ReceiveReminderMethodName = "ReceiveReminderAsync"; + private const string TimerMethodName = "FireTimerAsync"; + private readonly ActorRegistration registration; + private readonly ActorActivator activator; + private readonly ILoggerFactory loggerFactory; + private readonly IActorProxyFactory proxyFactory; + private readonly ActorTimerManager timerManager; + private readonly ConcurrentDictionary activeActors; + private readonly ActorMethodContext reminderMethodContext; + private readonly ActorMethodContext timerMethodContext; + private readonly ActorMessageSerializersManager serializersManager; + private readonly IActorMessageBodyFactory messageBodyFactory; + private readonly JsonSerializerOptions jsonSerializerOptions; + + // method dispatchermap used by remoting calls. + private readonly ActorMethodDispatcherMap methodDispatcherMap; + + // method info map used by non-remoting calls. + private readonly ActorMethodInfoMap actorMethodInfoMap; + + private readonly ILogger logger; + private IDaprInteractor daprInteractor { get; } + + + internal ActorManager( + ActorRegistration registration, + ActorActivator activator, + JsonSerializerOptions jsonSerializerOptions, + bool useJsonSerialization, + ILoggerFactory loggerFactory, + IActorProxyFactory proxyFactory, + IDaprInteractor daprInteractor) { - private const string ReceiveReminderMethodName = "ReceiveReminderAsync"; - private const string TimerMethodName = "FireTimerAsync"; - private readonly ActorRegistration registration; - private readonly ActorActivator activator; - private readonly ILoggerFactory loggerFactory; - private readonly IActorProxyFactory proxyFactory; - private readonly ActorTimerManager timerManager; - private readonly ConcurrentDictionary activeActors; - private readonly ActorMethodContext reminderMethodContext; - private readonly ActorMethodContext timerMethodContext; - private readonly ActorMessageSerializersManager serializersManager; - private readonly IActorMessageBodyFactory messageBodyFactory; - private readonly JsonSerializerOptions jsonSerializerOptions; - - // method dispatchermap used by remoting calls. - private readonly ActorMethodDispatcherMap methodDispatcherMap; - - // method info map used by non-remoting calls. - private readonly ActorMethodInfoMap actorMethodInfoMap; - - private readonly ILogger logger; - private IDaprInteractor daprInteractor { get; } - - - internal ActorManager( - ActorRegistration registration, - ActorActivator activator, - JsonSerializerOptions jsonSerializerOptions, - bool useJsonSerialization, - ILoggerFactory loggerFactory, - IActorProxyFactory proxyFactory, - IDaprInteractor daprInteractor) + this.registration = registration; + this.activator = activator; + this.jsonSerializerOptions = jsonSerializerOptions; + this.loggerFactory = loggerFactory; + this.proxyFactory = proxyFactory; + this.daprInteractor = daprInteractor; + + this.timerManager = new DefaultActorTimerManager(this.daprInteractor); + + // map for remoting calls. + this.methodDispatcherMap = new ActorMethodDispatcherMap(this.registration.Type.InterfaceTypes); + + // map for non-remoting calls. + this.actorMethodInfoMap = new ActorMethodInfoMap(this.registration.Type.InterfaceTypes); + this.activeActors = new ConcurrentDictionary(); + this.reminderMethodContext = ActorMethodContext.CreateForReminder(ReceiveReminderMethodName); + this.timerMethodContext = ActorMethodContext.CreateForTimer(TimerMethodName); + + // provide a serializer if 'useJsonSerialization' is true and no serialization provider is provided. + IActorMessageBodySerializationProvider serializationProvider = null; + if (useJsonSerialization) { - this.registration = registration; - this.activator = activator; - this.jsonSerializerOptions = jsonSerializerOptions; - this.loggerFactory = loggerFactory; - this.proxyFactory = proxyFactory; - this.daprInteractor = daprInteractor; - - this.timerManager = new DefaultActorTimerManager(this.daprInteractor); - - // map for remoting calls. - this.methodDispatcherMap = new ActorMethodDispatcherMap(this.registration.Type.InterfaceTypes); - - // map for non-remoting calls. - this.actorMethodInfoMap = new ActorMethodInfoMap(this.registration.Type.InterfaceTypes); - this.activeActors = new ConcurrentDictionary(); - this.reminderMethodContext = ActorMethodContext.CreateForReminder(ReceiveReminderMethodName); - this.timerMethodContext = ActorMethodContext.CreateForTimer(TimerMethodName); - - // provide a serializer if 'useJsonSerialization' is true and no serialization provider is provided. - IActorMessageBodySerializationProvider serializationProvider = null; - if (useJsonSerialization) - { - serializationProvider = new ActorMessageBodyJsonSerializationProvider(jsonSerializerOptions); - } + serializationProvider = new ActorMessageBodyJsonSerializationProvider(jsonSerializerOptions); + } - this.serializersManager = IntializeSerializationManager(serializationProvider); - this.messageBodyFactory = new WrappedRequestMessageFactory(); + this.serializersManager = IntializeSerializationManager(serializationProvider); + this.messageBodyFactory = new WrappedRequestMessageFactory(); - this.logger = loggerFactory.CreateLogger(this.GetType()); - } + this.logger = loggerFactory.CreateLogger(this.GetType()); + } - internal ActorTypeInformation ActorTypeInfo => this.registration.Type; + internal ActorTypeInformation ActorTypeInfo => this.registration.Type; - internal async Task> DispatchWithRemotingAsync(ActorId actorId, string actorMethodName, string daprActorheader, Stream data, CancellationToken cancellationToken) - { - var actorMethodContext = ActorMethodContext.CreateForActor(actorMethodName); + internal async Task> DispatchWithRemotingAsync(ActorId actorId, string actorMethodName, string daprActorheader, Stream data, CancellationToken cancellationToken) + { + var actorMethodContext = ActorMethodContext.CreateForActor(actorMethodName); - // Get the serialized header - var actorMessageHeader = this.serializersManager.GetHeaderSerializer() - .DeserializeRequestHeaders(new MemoryStream(Encoding.ASCII.GetBytes(daprActorheader))); + // Get the serialized header + var actorMessageHeader = this.serializersManager.GetHeaderSerializer() + .DeserializeRequestHeaders(new MemoryStream(Encoding.ASCII.GetBytes(daprActorheader))); - var interfaceId = actorMessageHeader.InterfaceId; + var interfaceId = actorMessageHeader.InterfaceId; - // Get the deserialized Body. - var msgBodySerializer = this.serializersManager.GetRequestMessageBodySerializer(actorMessageHeader.InterfaceId, actorMethodContext.MethodName); + // Get the deserialized Body. + var msgBodySerializer = this.serializersManager.GetRequestMessageBodySerializer(actorMessageHeader.InterfaceId, actorMethodContext.MethodName); - IActorRequestMessageBody actorMessageBody; - using (var stream = new MemoryStream()) - { - await data.CopyToAsync(stream); - actorMessageBody = await msgBodySerializer.DeserializeAsync(stream); - } - - // Call the method on the method dispatcher using the Func below. - var methodDispatcher = this.methodDispatcherMap.GetDispatcher(actorMessageHeader.InterfaceId); + IActorRequestMessageBody actorMessageBody; + using (var stream = new MemoryStream()) + { + await data.CopyToAsync(stream); + actorMessageBody = await msgBodySerializer.DeserializeAsync(stream); + } - // Create a Func to be invoked by common method. - async Task> RequestFunc(Actor actor, CancellationToken ct) - { - IActorResponseMessageBody responseMsgBody = null; + // Call the method on the method dispatcher using the Func below. + var methodDispatcher = this.methodDispatcherMap.GetDispatcher(actorMessageHeader.InterfaceId); - responseMsgBody = (IActorResponseMessageBody)await methodDispatcher.DispatchAsync( - actor, - actorMessageHeader.MethodId, - actorMessageBody, - this.messageBodyFactory, - ct); + // Create a Func to be invoked by common method. + async Task> RequestFunc(Actor actor, CancellationToken ct) + { + IActorResponseMessageBody responseMsgBody = null; - return this.CreateResponseMessage(responseMsgBody, interfaceId, actorMethodContext.MethodName); - } + responseMsgBody = (IActorResponseMessageBody)await methodDispatcher.DispatchAsync( + actor, + actorMessageHeader.MethodId, + actorMessageBody, + this.messageBodyFactory, + ct); - return await this.DispatchInternalAsync(actorId, actorMethodContext, RequestFunc, cancellationToken); + return this.CreateResponseMessage(responseMsgBody, interfaceId, actorMethodContext.MethodName); } - internal async Task DispatchWithoutRemotingAsync(ActorId actorId, string actorMethodName, Stream requestBodyStream, Stream responseBodyStream, CancellationToken cancellationToken) - { - var actorMethodContext = ActorMethodContext.CreateForActor(actorMethodName); - - // Create a Func to be invoked by common method. - var methodInfo = this.actorMethodInfoMap.LookupActorMethodInfo(actorMethodName); + return await this.DispatchInternalAsync(actorId, actorMethodContext, RequestFunc, cancellationToken); + } - async Task RequestFunc(Actor actor, CancellationToken ct) - { - var parameters = methodInfo.GetParameters(); - dynamic awaitable; + internal async Task DispatchWithoutRemotingAsync(ActorId actorId, string actorMethodName, Stream requestBodyStream, Stream responseBodyStream, CancellationToken cancellationToken) + { + var actorMethodContext = ActorMethodContext.CreateForActor(actorMethodName); - if (parameters.Length == 0 || (parameters.Length == 1 && parameters[0].ParameterType == typeof(CancellationToken))) - { - awaitable = methodInfo.Invoke(actor, parameters.Length == 0 ? null : new object[] { ct }); - } - else if (parameters.Length == 1 || (parameters.Length == 2 && parameters[1].ParameterType == typeof(CancellationToken))) - { - // deserialize using stream. - var type = parameters[0].ParameterType; - var deserializedType = await JsonSerializer.DeserializeAsync(requestBodyStream, type, jsonSerializerOptions); - awaitable = methodInfo.Invoke(actor, parameters.Length == 1 ? new object[] { deserializedType } : new object[] { deserializedType, ct }); - } - else - { - var errorMsg = $"Method {string.Concat(methodInfo.DeclaringType.Name, ".", methodInfo.Name)} has more than one parameter and can't be invoked through http"; - throw new ArgumentException(errorMsg); - } + // Create a Func to be invoked by common method. + var methodInfo = this.actorMethodInfoMap.LookupActorMethodInfo(actorMethodName); - await awaitable; + async Task RequestFunc(Actor actor, CancellationToken ct) + { + var parameters = methodInfo.GetParameters(); + dynamic awaitable; - // Handle the return type of method correctly. - if (methodInfo.ReturnType.Name != typeof(Task).Name) - { - // already await, Getting result will be non blocking. - var x = awaitable.GetAwaiter().GetResult(); - return x; - } - else - { - return default; - } + if (parameters.Length == 0 || (parameters.Length == 1 && parameters[0].ParameterType == typeof(CancellationToken))) + { + awaitable = methodInfo.Invoke(actor, parameters.Length == 0 ? null : new object[] { ct }); + } + else if (parameters.Length == 1 || (parameters.Length == 2 && parameters[1].ParameterType == typeof(CancellationToken))) + { + // deserialize using stream. + var type = parameters[0].ParameterType; + var deserializedType = await JsonSerializer.DeserializeAsync(requestBodyStream, type, jsonSerializerOptions); + awaitable = methodInfo.Invoke(actor, parameters.Length == 1 ? new object[] { deserializedType } : new object[] { deserializedType, ct }); + } + else + { + var errorMsg = $"Method {string.Concat(methodInfo.DeclaringType.Name, ".", methodInfo.Name)} has more than one parameter and can't be invoked through http"; + throw new ArgumentException(errorMsg); } - var result = await this.DispatchInternalAsync(actorId, actorMethodContext, RequestFunc, cancellationToken); + await awaitable; - // Write Response back if method's return type is other than Task. - // Serialize result if it has result (return type was not just Task.) + // Handle the return type of method correctly. if (methodInfo.ReturnType.Name != typeof(Task).Name) { -#if NET7_0_OR_GREATER - var resultType = methodInfo.ReturnType.GenericTypeArguments[0]; - await JsonSerializer.SerializeAsync(responseBodyStream, result, resultType, jsonSerializerOptions); -#else - await JsonSerializer.SerializeAsync(responseBodyStream, result, jsonSerializerOptions); -#endif - + // already await, Getting result will be non blocking. + var x = awaitable.GetAwaiter().GetResult(); + return x; + } + else + { + return default; } } - internal async Task FireReminderAsync(ActorId actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default) - { - // Only FireReminder if its IRemindable, else ignore it. - if (this.ActorTypeInfo.IsRemindable) - { - var reminderdata = await ReminderInfo.DeserializeAsync(requestBodyStream); + var result = await this.DispatchInternalAsync(actorId, actorMethodContext, RequestFunc, cancellationToken); - // Create a Func to be invoked by common method. - async Task RequestFunc(Actor actor, CancellationToken ct) - { - await - (actor as IRemindable).ReceiveReminderAsync( - reminderName, - reminderdata.Data, - reminderdata.DueTime, - reminderdata.Period); - - return null; - } + // Write Response back if method's return type is other than Task. + // Serialize result if it has result (return type was not just Task.) + if (methodInfo.ReturnType.Name != typeof(Task).Name) + { +#if NET7_0_OR_GREATER + var resultType = methodInfo.ReturnType.GenericTypeArguments[0]; + await JsonSerializer.SerializeAsync(responseBodyStream, result, resultType, jsonSerializerOptions); +#else + await JsonSerializer.SerializeAsync(responseBodyStream, result, jsonSerializerOptions); +#endif - await this.DispatchInternalAsync(actorId, this.reminderMethodContext, RequestFunc, cancellationToken); - } } + } - internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, CancellationToken cancellationToken = default) + internal async Task FireReminderAsync(ActorId actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default) + { + // Only FireReminder if its IRemindable, else ignore it. + if (this.ActorTypeInfo.IsRemindable) { - #pragma warning disable 0618 - var timerData = await JsonSerializer.DeserializeAsync(requestBodyStream); - #pragma warning restore 0618 + var reminderdata = await ReminderInfo.DeserializeAsync(requestBodyStream); // Create a Func to be invoked by common method. async Task RequestFunc(Actor actor, CancellationToken ct) { - var actorType = actor.Host.ActorTypeInfo.ImplementationType; - var methodInfo = actor.GetMethodInfoUsingReflection(actorType, timerData.Callback); - - var parameters = methodInfo.GetParameters(); - - // The timer callback routine needs to return a type Task - await (Task)(methodInfo.Invoke(actor, (parameters.Length == 0) ? null : new object[] { timerData.Data })); - - return default; + await + (actor as IRemindable).ReceiveReminderAsync( + reminderName, + reminderdata.Data, + reminderdata.DueTime, + reminderdata.Period); + + return null; } - await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken); + await this.DispatchInternalAsync(actorId, this.reminderMethodContext, RequestFunc, cancellationToken); } + } - internal async Task ActivateActorAsync(ActorId actorId) + internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, CancellationToken cancellationToken = default) + { +#pragma warning disable 0618 + var timerData = await JsonSerializer.DeserializeAsync(requestBodyStream); +#pragma warning restore 0618 + + // Create a Func to be invoked by common method. + async Task RequestFunc(Actor actor, CancellationToken ct) { - // An actor is activated by "Dapr" runtime when a call is to be made for an actor. - var state = await this.CreateActorAsync(actorId); + var actorType = actor.Host.ActorTypeInfo.ImplementationType; + var methodInfo = actor.GetMethodInfoUsingReflection(actorType, timerData.Callback); - try - { - await state.Actor.OnActivateInternalAsync(); - } - catch - { - // Ensure we don't leak resources if user-code throws during activation. - await DeleteActorAsync(state); - throw; - } + var parameters = methodInfo.GetParameters(); - // Add actor to activeActors only after OnActivate succeeds (user code can throw error from its override of Activate method.) - // - // In theory the Dapr runtime protects us from double-activation - there's no case - // where we *expect* to see the *update* code path taken. However it's a possiblity and - // we should handle it. - // - // The policy we have chosen is to always keep the registered instance if we hit a double-activation - // so that means we have to destroy the 'new' instance. - var current = this.activeActors.AddOrUpdate(actorId, state, (key, oldValue) => oldValue); - if (object.ReferenceEquals(state, current)) - { - // On this code path it was an *Add*. Nothing left to do. - return; - } + // The timer callback routine needs to return a type Task + await (Task)(methodInfo.Invoke(actor, (parameters.Length == 0) ? null : new object[] { timerData.Data })); - // On this code path it was an *Update*. We need to destroy the new instance and clean up. - await DeactivateActorCore(state); + return default; } - private async Task CreateActorAsync(ActorId actorId) - { - this.logger.LogDebug("Creating Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, actorId); - var host = new ActorHost(this.ActorTypeInfo, actorId, this.jsonSerializerOptions, this.loggerFactory, this.proxyFactory, this.timerManager) - { - StateProvider = new DaprStateProvider(this.daprInteractor, this.jsonSerializerOptions), - }; - var state = await this.activator.CreateAsync(host); - this.logger.LogDebug("Finished creating Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, actorId); - return state; - } + await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken); + } - internal async Task DeactivateActorAsync(ActorId actorId) + internal async Task ActivateActorAsync(ActorId actorId) + { + // An actor is activated by "Dapr" runtime when a call is to be made for an actor. + var state = await this.CreateActorAsync(actorId); + + try { - if (this.activeActors.TryRemove(actorId, out var deactivatedActor)) - { - await DeactivateActorCore(deactivatedActor); - } + await state.Actor.OnActivateInternalAsync(); } - - private async Task DeactivateActorCore(ActorActivatorState state) + catch { - try - { - await state.Actor.OnDeactivateInternalAsync(); - } - finally - { - // Ensure we don't leak resources if user-code throws during deactivation. - await DeleteActorAsync(state); - } + // Ensure we don't leak resources if user-code throws during activation. + await DeleteActorAsync(state); + throw; } - private async Task DeleteActorAsync(ActorActivatorState state) + // Add actor to activeActors only after OnActivate succeeds (user code can throw error from its override of Activate method.) + // + // In theory the Dapr runtime protects us from double-activation - there's no case + // where we *expect* to see the *update* code path taken. However it's a possiblity and + // we should handle it. + // + // The policy we have chosen is to always keep the registered instance if we hit a double-activation + // so that means we have to destroy the 'new' instance. + var current = this.activeActors.AddOrUpdate(actorId, state, (key, oldValue) => oldValue); + if (object.ReferenceEquals(state, current)) { - this.logger.LogDebug("Deleting Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, state.Actor.Id); - await this.activator.DeleteAsync(state); - this.logger.LogDebug("Finished deleting Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, state.Actor.Id); + // On this code path it was an *Add*. Nothing left to do. + return; } - // Used for testing - do not leak the actor instances outside of this method in library code. - public bool TryGetActorAsync(ActorId id, out Actor actor) + // On this code path it was an *Update*. We need to destroy the new instance and clean up. + await DeactivateActorCore(state); + } + + private async Task CreateActorAsync(ActorId actorId) + { + this.logger.LogDebug("Creating Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, actorId); + var host = new ActorHost(this.ActorTypeInfo, actorId, this.jsonSerializerOptions, this.loggerFactory, this.proxyFactory, this.timerManager) { - var found = this.activeActors.TryGetValue(id, out var state); - actor = found ? state.Actor : default; - return found; - } + StateProvider = new DaprStateProvider(this.daprInteractor, this.jsonSerializerOptions), + }; + var state = await this.activator.CreateAsync(host); + this.logger.LogDebug("Finished creating Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, actorId); + return state; + } - private static ActorMessageSerializersManager IntializeSerializationManager( - IActorMessageBodySerializationProvider serializationProvider) + internal async Task DeactivateActorAsync(ActorId actorId) + { + if (this.activeActors.TryRemove(actorId, out var deactivatedActor)) { - // TODO serializer settings - return new ActorMessageSerializersManager( - serializationProvider, - new ActorMessageHeaderSerializer()); + await DeactivateActorCore(deactivatedActor); } + } - private async Task DispatchInternalAsync(ActorId actorId, ActorMethodContext actorMethodContext, Func> actorFunc, CancellationToken cancellationToken) + private async Task DeactivateActorCore(ActorActivatorState state) + { + try { - if (!this.activeActors.ContainsKey(actorId)) - { - await this.ActivateActorAsync(actorId); - } + await state.Actor.OnDeactivateInternalAsync(); + } + finally + { + // Ensure we don't leak resources if user-code throws during deactivation. + await DeleteActorAsync(state); + } + } - if (!this.activeActors.TryGetValue(actorId, out var state)) - { - var errorMsg = $"Actor {actorId} is not yet activated."; - throw new InvalidOperationException(errorMsg); - } + private async Task DeleteActorAsync(ActorActivatorState state) + { + this.logger.LogDebug("Deleting Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, state.Actor.Id); + await this.activator.DeleteAsync(state); + this.logger.LogDebug("Finished deleting Actor of type {ActorType} with ActorId {ActorId}", this.ActorTypeInfo.ImplementationType, state.Actor.Id); + } - var actor = state.Actor; + // Used for testing - do not leak the actor instances outside of this method in library code. + public bool TryGetActorAsync(ActorId id, out Actor actor) + { + var found = this.activeActors.TryGetValue(id, out var state); + actor = found ? state.Actor : default; + return found; + } - T retval; - try - { - // Set the state context of the request, if required and possible. - if (ActorReentrancyContextAccessor.ReentrancyContext != null) - { - if (state.Actor.StateManager is IActorContextualState contextualStateManager) - { - await contextualStateManager.SetStateContext(Guid.NewGuid().ToString()); - } - } + private static ActorMessageSerializersManager IntializeSerializationManager( + IActorMessageBodySerializationProvider serializationProvider) + { + // TODO serializer settings + return new ActorMessageSerializersManager( + serializationProvider, + new ActorMessageHeaderSerializer()); + } - // invoke the function of the actor - await actor.OnPreActorMethodAsyncInternal(actorMethodContext); - retval = await actorFunc.Invoke(actor, cancellationToken); + private async Task DispatchInternalAsync(ActorId actorId, ActorMethodContext actorMethodContext, Func> actorFunc, CancellationToken cancellationToken) + { + if (!this.activeActors.ContainsKey(actorId)) + { + await this.ActivateActorAsync(actorId); + } - // PostActivate will save the state, its not invoked when actorFunc invocation throws. - await actor.OnPostActorMethodAsyncInternal(actorMethodContext); - } - catch (Exception e) - { - await actor.OnActorMethodFailedInternalAsync(actorMethodContext, e); - throw; - } - finally + if (!this.activeActors.TryGetValue(actorId, out var state)) + { + var errorMsg = $"Actor {actorId} is not yet activated."; + throw new InvalidOperationException(errorMsg); + } + + var actor = state.Actor; + + T retval; + try + { + // Set the state context of the request, if required and possible. + if (ActorReentrancyContextAccessor.ReentrancyContext != null) { - // Set the state context of the request, if possible. if (state.Actor.StateManager is IActorContextualState contextualStateManager) { - await contextualStateManager.SetStateContext(null); + await contextualStateManager.SetStateContext(Guid.NewGuid().ToString()); } } - return retval; - } + // invoke the function of the actor + await actor.OnPreActorMethodAsyncInternal(actorMethodContext); + retval = await actorFunc.Invoke(actor, cancellationToken); - private Tuple CreateResponseMessage(IActorResponseMessageBody msgBody, int interfaceId, string methodName) + // PostActivate will save the state, its not invoked when actorFunc invocation throws. + await actor.OnPostActorMethodAsyncInternal(actorMethodContext); + } + catch (Exception e) { - var responseMsgBodyBytes = Array.Empty(); - if (msgBody != null) + await actor.OnActorMethodFailedInternalAsync(actorMethodContext, e); + throw; + } + finally + { + // Set the state context of the request, if possible. + if (state.Actor.StateManager is IActorContextualState contextualStateManager) { - var responseSerializer = this.serializersManager.GetResponseMessageBodySerializer(interfaceId, methodName); - responseMsgBodyBytes = responseSerializer.Serialize(msgBody); + await contextualStateManager.SetStateContext(null); } + } + + return retval; + } - return new Tuple(string.Empty, responseMsgBodyBytes); + private Tuple CreateResponseMessage(IActorResponseMessageBody msgBody, int interfaceId, string methodName) + { + var responseMsgBodyBytes = Array.Empty(); + if (msgBody != null) + { + var responseSerializer = this.serializersManager.GetResponseMessageBodySerializer(interfaceId, methodName); + responseMsgBodyBytes = responseSerializer.Serialize(msgBody); } + + return new Tuple(string.Empty, responseMsgBodyBytes); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorMethodContext.cs b/src/Dapr.Actors/Runtime/ActorMethodContext.cs index 4e07633b0..821399dcc 100644 --- a/src/Dapr.Actors/Runtime/ActorMethodContext.cs +++ b/src/Dapr.Actors/Runtime/ActorMethodContext.cs @@ -11,56 +11,55 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Contains information about the method that is invoked by actor runtime and +/// is passed as an argument to and . +/// +public struct ActorMethodContext { + private readonly string actorMethodName; + private readonly ActorCallType actorCallType; + + private ActorMethodContext(string methodName, ActorCallType callType) + { + this.actorMethodName = methodName; + this.actorCallType = callType; + } + /// - /// Contains information about the method that is invoked by actor runtime and - /// is passed as an argument to and . + /// Gets the name of the method invoked by actor runtime. /// - public struct ActorMethodContext + /// The name of method. + public string MethodName { - private readonly string actorMethodName; - private readonly ActorCallType actorCallType; - - private ActorMethodContext(string methodName, ActorCallType callType) - { - this.actorMethodName = methodName; - this.actorCallType = callType; - } - - /// - /// Gets the name of the method invoked by actor runtime. - /// - /// The name of method. - public string MethodName - { - get { return this.actorMethodName; } - } + get { return this.actorMethodName; } + } - /// - /// Gets the type of call by actor runtime (e.g. actor interface method, timer callback etc.). - /// - /// - /// An representing the call type. - /// - public ActorCallType CallType - { - get { return this.actorCallType; } - } + /// + /// Gets the type of call by actor runtime (e.g. actor interface method, timer callback etc.). + /// + /// + /// An representing the call type. + /// + public ActorCallType CallType + { + get { return this.actorCallType; } + } - internal static ActorMethodContext CreateForActor(string methodName) - { - return new ActorMethodContext(methodName, ActorCallType.ActorInterfaceMethod); - } + internal static ActorMethodContext CreateForActor(string methodName) + { + return new ActorMethodContext(methodName, ActorCallType.ActorInterfaceMethod); + } - internal static ActorMethodContext CreateForTimer(string methodName) - { - return new ActorMethodContext(methodName, ActorCallType.TimerMethod); - } + internal static ActorMethodContext CreateForTimer(string methodName) + { + return new ActorMethodContext(methodName, ActorCallType.TimerMethod); + } - internal static ActorMethodContext CreateForReminder(string methodName) - { - return new ActorMethodContext(methodName, ActorCallType.ReminderMethod); - } + internal static ActorMethodContext CreateForReminder(string methodName) + { + return new ActorMethodContext(methodName, ActorCallType.ReminderMethod); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorMethodInfoMap.cs b/src/Dapr.Actors/Runtime/ActorMethodInfoMap.cs index 0085e819b..a677fbb7a 100644 --- a/src/Dapr.Actors/Runtime/ActorMethodInfoMap.cs +++ b/src/Dapr.Actors/Runtime/ActorMethodInfoMap.cs @@ -11,41 +11,40 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +using System; +using System.Collections.Generic; +using System.Reflection; + +/// +/// Actor method dispatcher map for non remoting calls. method_name -> MethodInfo for methods defined in IACtor interfaces. +/// +internal class ActorMethodInfoMap { - using System; - using System.Collections.Generic; - using System.Reflection; + private readonly Dictionary methods; - /// - /// Actor method dispatcher map for non remoting calls. method_name -> MethodInfo for methods defined in IACtor interfaces. - /// - internal class ActorMethodInfoMap + public ActorMethodInfoMap(IEnumerable interfaceTypes) { - private readonly Dictionary methods; + this.methods = new Dictionary(); - public ActorMethodInfoMap(IEnumerable interfaceTypes) + // Find methods which are defined in IActor interface. + foreach (var actorInterface in interfaceTypes) { - this.methods = new Dictionary(); - - // Find methods which are defined in IActor interface. - foreach (var actorInterface in interfaceTypes) + foreach (var methodInfo in actorInterface.GetMethods()) { - foreach (var methodInfo in actorInterface.GetMethods()) - { - this.methods.Add(methodInfo.Name, methodInfo); - } + this.methods.Add(methodInfo.Name, methodInfo); } } + } - public MethodInfo LookupActorMethodInfo(string methodName) + public MethodInfo LookupActorMethodInfo(string methodName) + { + if (!this.methods.TryGetValue(methodName, out var methodInfo)) { - if (!this.methods.TryGetValue(methodName, out var methodInfo)) - { - throw new MissingMethodException($"Actor type doesn't contain method {methodName}"); - } - - return methodInfo; + throw new MissingMethodException($"Actor type doesn't contain method {methodName}"); } + + return methodInfo; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorRegistration.cs b/src/Dapr.Actors/Runtime/ActorRegistration.cs index b8ea73226..279ed0325 100644 --- a/src/Dapr.Actors/Runtime/ActorRegistration.cs +++ b/src/Dapr.Actors/Runtime/ActorRegistration.cs @@ -11,47 +11,46 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents an actor type registered with the runtime. Provides access to per-type +/// options for the actor. +/// +public sealed class ActorRegistration { /// - /// Represents an actor type registered with the runtime. Provides access to per-type - /// options for the actor. + /// Initializes a new instance of . /// - public sealed class ActorRegistration + /// The for the actor type. + public ActorRegistration(ActorTypeInformation type) : this(type, null) { - /// - /// Initializes a new instance of . - /// - /// The for the actor type. - public ActorRegistration(ActorTypeInformation type) : this(type, null) - { - } + } - /// - /// Initializes a new instance of . - /// - /// The for the actor type. - /// The optional that are specified for this type only. - public ActorRegistration(ActorTypeInformation type, ActorRuntimeOptions options) - { - this.Type = type; - this.TypeOptions = options; - } + /// + /// Initializes a new instance of . + /// + /// The for the actor type. + /// The optional that are specified for this type only. + public ActorRegistration(ActorTypeInformation type, ActorRuntimeOptions options) + { + this.Type = type; + this.TypeOptions = options; + } - /// - /// Gets the for the actor type. - /// - public ActorTypeInformation Type { get; } + /// + /// Gets the for the actor type. + /// + public ActorTypeInformation Type { get; } - /// - /// Gets or sets the to use for the actor. If not set the default - /// activator of the runtime will be used. - /// - public ActorActivator Activator { get; set; } + /// + /// Gets or sets the to use for the actor. If not set the default + /// activator of the runtime will be used. + /// + public ActorActivator Activator { get; set; } - /// - /// An optional set of options for this specific actor type. These will override the top level or default values. - /// - public ActorRuntimeOptions TypeOptions { get; } - } -} + /// + /// An optional set of options for this specific actor type. These will override the top level or default values. + /// + public ActorRuntimeOptions TypeOptions { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs index 69c01646d..6eb48154c 100644 --- a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs +++ b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs @@ -14,74 +14,73 @@ using System; using System.Collections.ObjectModel; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// A collection of instances. +/// +public sealed class ActorRegistrationCollection : KeyedCollection { /// - /// A collection of instances. + /// Returns the key for the item. /// - public sealed class ActorRegistrationCollection : KeyedCollection + /// The item. + /// The key. + protected override ActorTypeInformation GetKeyForItem(ActorRegistration item) { - /// - /// Returns the key for the item. - /// - /// The item. - /// The key. - protected override ActorTypeInformation GetKeyForItem(ActorRegistration item) - { - return item.Type; - } + return item.Type; + } - /// - /// Registers an actor type in the collection. - /// - /// Type of actor. - /// An optional delegate used to configure the actor registration. - public void RegisterActor(Action configure = null) - where TActor : Actor - { - RegisterActor(actorTypeName: null, configure); - } + /// + /// Registers an actor type in the collection. + /// + /// Type of actor. + /// An optional delegate used to configure the actor registration. + public void RegisterActor(Action configure = null) + where TActor : Actor + { + RegisterActor(actorTypeName: null, configure); + } - /// - /// Registers an actor type in the collection. - /// - /// Type of actor. - /// An optional that defines values for this type alone. - /// An optional delegate used to configure the actor registration. - public void RegisterActor(ActorRuntimeOptions typeOptions, Action configure = null) - where TActor : Actor - { - RegisterActor(null, typeOptions, configure); - } + /// + /// Registers an actor type in the collection. + /// + /// Type of actor. + /// An optional that defines values for this type alone. + /// An optional delegate used to configure the actor registration. + public void RegisterActor(ActorRuntimeOptions typeOptions, Action configure = null) + where TActor : Actor + { + RegisterActor(null, typeOptions, configure); + } - /// - /// Registers an actor type in the collection. - /// - /// Type of actor. - /// The name of the actor type represented by the actor. - /// An optional delegate used to configure the actor registration. - /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . - public void RegisterActor(string actorTypeName, Action configure = null) - where TActor : Actor - { - RegisterActor(actorTypeName, null, configure); - } + /// + /// Registers an actor type in the collection. + /// + /// Type of actor. + /// The name of the actor type represented by the actor. + /// An optional delegate used to configure the actor registration. + /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . + public void RegisterActor(string actorTypeName, Action configure = null) + where TActor : Actor + { + RegisterActor(actorTypeName, null, configure); + } - /// - /// Registers an actor type in the collection. - /// - /// Type of actor. - /// The name of the actor type represented by the actor. - /// An optional that defines values for this type alone. - /// An optional delegate used to configure the actor registration. - /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . - public void RegisterActor(string actorTypeName, ActorRuntimeOptions typeOptions, Action configure = null) - where TActor : Actor - { - var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor), actorTypeName); - var registration = new ActorRegistration(actorTypeInfo, typeOptions); - configure?.Invoke(registration); - this.Add(registration); - } + /// + /// Registers an actor type in the collection. + /// + /// Type of actor. + /// The name of the actor type represented by the actor. + /// An optional that defines values for this type alone. + /// An optional delegate used to configure the actor registration. + /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . + public void RegisterActor(string actorTypeName, ActorRuntimeOptions typeOptions, Action configure = null) + where TActor : Actor + { + var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor), actorTypeName); + var registration = new ActorRegistration(actorTypeInfo, typeOptions); + configure?.Invoke(registration); + this.Add(registration); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorReminder.cs b/src/Dapr.Actors/Runtime/ActorReminder.cs index 930df29b8..8dea2a11f 100644 --- a/src/Dapr.Actors/Runtime/ActorReminder.cs +++ b/src/Dapr.Actors/Runtime/ActorReminder.cs @@ -16,212 +16,211 @@ using System.Threading; using Dapr.Actors.Resources; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents a reminder registered by an actor. +/// +public class ActorReminder : ActorReminderToken, IActorReminder { + private static readonly TimeSpan MiniumPeriod = Timeout.InfiniteTimeSpan; + /// - /// Represents a reminder registered by an actor. + /// Initializes a new instance of . /// - public class ActorReminder : ActorReminderToken, IActorReminder + /// The actor type. + /// The actor id. + /// The reminder name. + /// The state associated with the reminder. + /// The reminder due time. + /// The reminder period. + public ActorReminder( + string actorType, + ActorId actorId, + string name, + byte[] state, + TimeSpan dueTime, + TimeSpan period) + : this(new ActorReminderOptions + { + ActorTypeName = actorType, + Id = actorId, + ReminderName = name, + State = state, + DueTime = dueTime, + Period = period, + Ttl = null + }) { - private static readonly TimeSpan MiniumPeriod = Timeout.InfiniteTimeSpan; + } - /// - /// Initializes a new instance of . - /// - /// The actor type. - /// The actor id. - /// The reminder name. - /// The state associated with the reminder. - /// The reminder due time. - /// The reminder period. - public ActorReminder( - string actorType, - ActorId actorId, - string name, - byte[] state, - TimeSpan dueTime, - TimeSpan period) - : this(new ActorReminderOptions - { - ActorTypeName = actorType, - Id = actorId, - ReminderName = name, - State = state, - DueTime = dueTime, - Period = period, - Ttl = null - }) + /// + /// Initializes a new instance of . + /// + /// The actor type. + /// The actor id. + /// The reminder name. + /// The state associated with the reminder. + /// The reminder due time. + /// The reminder period. + /// The reminder ttl. + public ActorReminder( + string actorType, + ActorId actorId, + string name, + byte[] state, + TimeSpan dueTime, + TimeSpan period, + TimeSpan ttl) + : this(new ActorReminderOptions { - } + ActorTypeName = actorType, + Id = actorId, + ReminderName = name, + State = state, + DueTime = dueTime, + Period = period, + Ttl = ttl + }) + { + } - /// - /// Initializes a new instance of . - /// - /// The actor type. - /// The actor id. - /// The reminder name. - /// The state associated with the reminder. - /// The reminder due time. - /// The reminder period. - /// The reminder ttl. - public ActorReminder( - string actorType, - ActorId actorId, - string name, - byte[] state, - TimeSpan dueTime, - TimeSpan period, - TimeSpan ttl) - : this(new ActorReminderOptions - { - ActorTypeName = actorType, - Id = actorId, - ReminderName = name, - State = state, - DueTime = dueTime, - Period = period, - Ttl = ttl - }) + /// + /// Initializes a new instance of . + /// + /// The actor type. + /// The actor id. + /// The reminder name. + /// The state associated with the reminder. + /// The reminder due time. + /// The reminder period. + /// The number of times reminder should be invoked. + /// The reminder ttl. + public ActorReminder( + string actorType, + ActorId actorId, + string name, + byte[] state, + TimeSpan dueTime, + TimeSpan period, + int? repetitions, + TimeSpan? ttl) + : this(new ActorReminderOptions { - } + ActorTypeName = actorType, + Id = actorId, + ReminderName = name, + State = state, + DueTime = dueTime, + Period = period, + Repetitions = repetitions, + Ttl = ttl + }) + { + } - /// - /// Initializes a new instance of . - /// - /// The actor type. - /// The actor id. - /// The reminder name. - /// The state associated with the reminder. - /// The reminder due time. - /// The reminder period. - /// The number of times reminder should be invoked. - /// The reminder ttl. - public ActorReminder( - string actorType, - ActorId actorId, - string name, - byte[] state, - TimeSpan dueTime, - TimeSpan period, - int? repetitions, - TimeSpan? ttl) - : this(new ActorReminderOptions - { - ActorTypeName = actorType, - Id = actorId, - ReminderName = name, - State = state, - DueTime = dueTime, - Period = period, - Repetitions = repetitions, - Ttl = ttl - }) + /// + /// Initializes a new instance of . + /// + /// The actor type. + /// The actor id. + /// The reminder name. + /// The state associated with the reminder. + /// The reminder due time. + /// The reminder period. + /// The number of times reminder should be invoked. + public ActorReminder( + string actorType, + ActorId actorId, + string name, + byte[] state, + TimeSpan dueTime, + TimeSpan period, + int? repetitions) + : this(new ActorReminderOptions { - } + ActorTypeName = actorType, + Id = actorId, + ReminderName = name, + State = state, + DueTime = dueTime, + Period = period, + Repetitions = repetitions + }) + { + } - /// - /// Initializes a new instance of . - /// - /// The actor type. - /// The actor id. - /// The reminder name. - /// The state associated with the reminder. - /// The reminder due time. - /// The reminder period. - /// The number of times reminder should be invoked. - public ActorReminder( - string actorType, - ActorId actorId, - string name, - byte[] state, - TimeSpan dueTime, - TimeSpan period, - int? repetitions) - : this(new ActorReminderOptions - { - ActorTypeName = actorType, - Id = actorId, - ReminderName = name, - State = state, - DueTime = dueTime, - Period = period, - Repetitions = repetitions - }) + /// + /// Initializes a new instance of . + /// + /// A containing the various settings for an . + internal ActorReminder(ActorReminderOptions options) + : base(options.ActorTypeName, options.Id, options.ReminderName) + { + if (options.DueTime < TimeSpan.Zero) { + throw new ArgumentOutOfRangeException(nameof(options.DueTime), string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + TimeSpan.Zero.TotalMilliseconds, + TimeSpan.MaxValue.TotalMilliseconds)); } - /// - /// Initializes a new instance of . - /// - /// A containing the various settings for an . - internal ActorReminder(ActorReminderOptions options) - : base(options.ActorTypeName, options.Id, options.ReminderName) + if (options.Period < MiniumPeriod) { - if (options.DueTime < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(options.DueTime), string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - TimeSpan.Zero.TotalMilliseconds, - TimeSpan.MaxValue.TotalMilliseconds)); - } - - if (options.Period < MiniumPeriod) - { - throw new ArgumentOutOfRangeException(nameof(options.Period), string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - MiniumPeriod.TotalMilliseconds, - TimeSpan.MaxValue.TotalMilliseconds)); - } + throw new ArgumentOutOfRangeException(nameof(options.Period), string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + MiniumPeriod.TotalMilliseconds, + TimeSpan.MaxValue.TotalMilliseconds)); + } - if (options.Ttl != null && (options.Ttl < options.DueTime || options.Ttl < TimeSpan.Zero)) - { - throw new ArgumentOutOfRangeException(nameof(options.Ttl), string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - options.DueTime, - TimeSpan.MaxValue.TotalMilliseconds)); - } + if (options.Ttl != null && (options.Ttl < options.DueTime || options.Ttl < TimeSpan.Zero)) + { + throw new ArgumentOutOfRangeException(nameof(options.Ttl), string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + options.DueTime, + TimeSpan.MaxValue.TotalMilliseconds)); + } - if (options.Repetitions != null && options.Repetitions <= 0) - { - throw new ArgumentOutOfRangeException(nameof(options.Repetitions), string.Format( - CultureInfo.CurrentCulture, - SR.RepetitionsArgumentOutOfRange, - options.Repetitions)); - } - - this.State = options.State; - this.DueTime = options.DueTime; - this.Period = options.Period; - this.Ttl = options.Ttl; - this.Repetitions = options.Repetitions; + if (options.Repetitions != null && options.Repetitions <= 0) + { + throw new ArgumentOutOfRangeException(nameof(options.Repetitions), string.Format( + CultureInfo.CurrentCulture, + SR.RepetitionsArgumentOutOfRange, + options.Repetitions)); } - /// - /// Gets the reminder state. - /// - public byte[] State { get; } + this.State = options.State; + this.DueTime = options.DueTime; + this.Period = options.Period; + this.Ttl = options.Ttl; + this.Repetitions = options.Repetitions; + } + + /// + /// Gets the reminder state. + /// + public byte[] State { get; } - /// - /// Gets the reminder due time. - /// - public TimeSpan DueTime { get; } + /// + /// Gets the reminder due time. + /// + public TimeSpan DueTime { get; } - /// - /// Gets the reminder period. - /// - public TimeSpan Period { get; } + /// + /// Gets the reminder period. + /// + public TimeSpan Period { get; } - /// - /// The optional that states when the reminder will expire. - /// - public TimeSpan? Ttl { get; } + /// + /// The optional that states when the reminder will expire. + /// + public TimeSpan? Ttl { get; } - /// - /// The optional property that gets the number of invocations of the reminder left. - /// - public int? Repetitions { get; } - } -} + /// + /// The optional property that gets the number of invocations of the reminder left. + /// + public int? Repetitions { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorReminderOptions.cs b/src/Dapr.Actors/Runtime/ActorReminderOptions.cs index 5d81d68fa..b27bd1574 100644 --- a/src/Dapr.Actors/Runtime/ActorReminderOptions.cs +++ b/src/Dapr.Actors/Runtime/ActorReminderOptions.cs @@ -1,49 +1,48 @@ using System; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// A collection of options used to create an . +/// +internal class ActorReminderOptions { /// - /// A collection of options used to create an . + /// The name of the type of the Actor that the reminder will fire for. /// - internal class ActorReminderOptions - { - /// - /// The name of the type of the Actor that the reminder will fire for. - /// - public string ActorTypeName { get; set; } + public string ActorTypeName { get; set; } - /// - /// The that the reminder will fire for. - /// - public ActorId Id { get; set; } + /// + /// The that the reminder will fire for. + /// + public ActorId Id { get; set; } - /// - /// The name of the reminder. - /// - public string ReminderName { get; set; } + /// + /// The name of the reminder. + /// + public string ReminderName { get; set; } - /// - /// State that is passed to the Actor when the reminder fires. - /// - public byte[] State { get; set; } + /// + /// State that is passed to the Actor when the reminder fires. + /// + public byte[] State { get; set; } - /// - /// that determines when the reminder will first fire. - /// - public TimeSpan DueTime { get; set; } + /// + /// that determines when the reminder will first fire. + /// + public TimeSpan DueTime { get; set; } - /// - /// that determines how much time there is between the reminder firing. Starts after the . - /// - public TimeSpan Period { get; set; } + /// + /// that determines how much time there is between the reminder firing. Starts after the . + /// + public TimeSpan Period { get; set; } - /// - /// An optional that determines when the reminder will expire. - /// - public TimeSpan? Ttl { get; set; } + /// + /// An optional that determines when the reminder will expire. + /// + public TimeSpan? Ttl { get; set; } - /// - /// The number of repetitions for which the reminder should be invoked. - /// - public int? Repetitions { get; set; } - } -} + /// + /// The number of repetitions for which the reminder should be invoked. + /// + public int? Repetitions { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorReminderToken.cs b/src/Dapr.Actors/Runtime/ActorReminderToken.cs index 207fe3fe2..a9f601eeb 100644 --- a/src/Dapr.Actors/Runtime/ActorReminderToken.cs +++ b/src/Dapr.Actors/Runtime/ActorReminderToken.cs @@ -13,57 +13,56 @@ using System; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents a reminder that can be unregistered by an actor. +/// +public class ActorReminderToken { /// - /// Represents a reminder that can be unregistered by an actor. + /// Initializes a new instance of . /// - public class ActorReminderToken + /// The actor type. + /// The actor id. + /// The reminder name. + public ActorReminderToken( + string actorType, + ActorId actorId, + string name) { - /// - /// Initializes a new instance of . - /// - /// The actor type. - /// The actor id. - /// The reminder name. - public ActorReminderToken( - string actorType, - ActorId actorId, - string name) + if (actorType == null) { - if (actorType == null) - { - throw new ArgumentNullException(nameof(actorType)); - } - - if (actorId == null) - { - throw new ArgumentNullException(nameof(actorId)); - } + throw new ArgumentNullException(nameof(actorType)); + } - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } + if (actorId == null) + { + throw new ArgumentNullException(nameof(actorId)); + } - this.ActorType = actorType; - this.ActorId = actorId; - this.Name = name; + if (name == null) + { + throw new ArgumentNullException(nameof(name)); } - /// - /// Gets the actor type. - /// - public string ActorType { get; } + this.ActorType = actorType; + this.ActorId = actorId; + this.Name = name; + } - /// - /// Gets the actor id. - /// - public ActorId ActorId { get; } + /// + /// Gets the actor type. + /// + public string ActorType { get; } - /// - /// Gets the reminder name. - /// - public string Name { get; } - } -} + /// + /// Gets the actor id. + /// + public ActorId ActorId { get; } + + /// + /// Gets the reminder name. + /// + public string Name { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorRuntime.cs b/src/Dapr.Actors/Runtime/ActorRuntime.cs index 7f01fefb7..6c8239efd 100644 --- a/src/Dapr.Actors/Runtime/ActorRuntime.cs +++ b/src/Dapr.Actors/Runtime/ActorRuntime.cs @@ -22,183 +22,182 @@ using Dapr.Actors.Client; using Microsoft.Extensions.Logging; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Contains methods to register actor types. Registering the types allows the runtime to create instances of the actor. +/// +public sealed class ActorRuntime { - /// - /// Contains methods to register actor types. Registering the types allows the runtime to create instances of the actor. - /// - public sealed class ActorRuntime + // Map of ActorType --> ActorManager. + private readonly Dictionary actorManagers = new Dictionary(); + private readonly ActorRuntimeOptions options; + private readonly ILogger logger; + private readonly ActorActivatorFactory activatorFactory; + private readonly IActorProxyFactory proxyFactory; + + internal ActorRuntime(ActorRuntimeOptions options, ILoggerFactory loggerFactory, ActorActivatorFactory activatorFactory, IActorProxyFactory proxyFactory) { - // Map of ActorType --> ActorManager. - private readonly Dictionary actorManagers = new Dictionary(); - private readonly ActorRuntimeOptions options; - private readonly ILogger logger; - private readonly ActorActivatorFactory activatorFactory; - private readonly IActorProxyFactory proxyFactory; - - internal ActorRuntime(ActorRuntimeOptions options, ILoggerFactory loggerFactory, ActorActivatorFactory activatorFactory, IActorProxyFactory proxyFactory) + this.options = options; + this.logger = loggerFactory.CreateLogger(this.GetType()); + this.activatorFactory = activatorFactory; + this.proxyFactory = proxyFactory; + + // Loop through actor registrations and create the actor manager for each one. + // We do this up front so that we can catch initialization errors early, and so + // that access to state can have a simple threading model. + // + // Revisit this if actor initialization becomes a significant source of delay for large projects. + foreach (var actor in options.Actors) { - this.options = options; - this.logger = loggerFactory.CreateLogger(this.GetType()); - this.activatorFactory = activatorFactory; - this.proxyFactory = proxyFactory; - - // Loop through actor registrations and create the actor manager for each one. - // We do this up front so that we can catch initialization errors early, and so - // that access to state can have a simple threading model. - // - // Revisit this if actor initialization becomes a significant source of delay for large projects. - foreach (var actor in options.Actors) - { - var daprInteractor = new DaprHttpInteractor(clientHandler: null, httpEndpoint: options.HttpEndpoint, apiToken: options.DaprApiToken, requestTimeout: null); - this.actorManagers[actor.Type.ActorTypeName] = new ActorManager( - actor, - actor.Activator ?? this.activatorFactory.CreateActivator(actor.Type), - this.options.JsonSerializerOptions, - this.options.UseJsonSerialization, - loggerFactory, - proxyFactory, - daprInteractor); - } + var daprInteractor = new DaprHttpInteractor(clientHandler: null, httpEndpoint: options.HttpEndpoint, apiToken: options.DaprApiToken, requestTimeout: null); + this.actorManagers[actor.Type.ActorTypeName] = new ActorManager( + actor, + actor.Activator ?? this.activatorFactory.CreateActivator(actor.Type), + this.options.JsonSerializerOptions, + this.options.UseJsonSerialization, + loggerFactory, + proxyFactory, + daprInteractor); } + } - /// - /// Gets actor registrations registered with the runtime. - /// - public IReadOnlyList RegisteredActors => this.options.Actors; + /// + /// Gets actor registrations registered with the runtime. + /// + public IReadOnlyList RegisteredActors => this.options.Actors; - internal Task SerializeSettingsAndRegisteredTypes(IBufferWriter output) - { - using Utf8JsonWriter writer = new Utf8JsonWriter(output); - writer.WriteStartObject(); + internal Task SerializeSettingsAndRegisteredTypes(IBufferWriter output) + { + using Utf8JsonWriter writer = new Utf8JsonWriter(output); + writer.WriteStartObject(); - writer.WritePropertyName("entities"); - writer.WriteStartArray(); + writer.WritePropertyName("entities"); + writer.WriteStartArray(); - foreach (var actor in this.RegisteredActors) - { - writer.WriteStringValue(actor.Type.ActorTypeName); - } + foreach (var actor in this.RegisteredActors) + { + writer.WriteStringValue(actor.Type.ActorTypeName); + } - writer.WriteEndArray(); + writer.WriteEndArray(); - writeActorOptions(writer, this.options); + writeActorOptions(writer, this.options); - var actorsWithConfigs = this.options.Actors.Where(actor => actor.TypeOptions != null).ToList(); + var actorsWithConfigs = this.options.Actors.Where(actor => actor.TypeOptions != null).ToList(); - if (actorsWithConfigs.Count > 0) + if (actorsWithConfigs.Count > 0) + { + writer.WritePropertyName("entitiesConfig"); + writer.WriteStartArray(); + foreach (var actor in actorsWithConfigs) { - writer.WritePropertyName("entitiesConfig"); + writer.WriteStartObject(); + writer.WritePropertyName("entities"); writer.WriteStartArray(); - foreach (var actor in actorsWithConfigs) - { - writer.WriteStartObject(); - writer.WritePropertyName("entities"); - writer.WriteStartArray(); - writer.WriteStringValue(actor.Type.ActorTypeName); - writer.WriteEndArray(); - - writeActorOptions(writer, actor.TypeOptions); - - writer.WriteEndObject(); - } + writer.WriteStringValue(actor.Type.ActorTypeName); writer.WriteEndArray(); - } - writer.WriteEndObject(); - return writer.FlushAsync(); - } + writeActorOptions(writer, actor.TypeOptions); - private void writeActorOptions(Utf8JsonWriter writer, ActorRuntimeOptions actorOptions) - { - if (actorOptions.ActorIdleTimeout != null) - { - writer.WriteString("actorIdleTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.ActorIdleTimeout)); + writer.WriteEndObject(); } + writer.WriteEndArray(); + } - if (actorOptions.ActorScanInterval != null) - { - writer.WriteString("actorScanInterval", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.ActorScanInterval)); - } + writer.WriteEndObject(); + return writer.FlushAsync(); + } - if (actorOptions.DrainOngoingCallTimeout != null) - { - writer.WriteString("drainOngoingCallTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.DrainOngoingCallTimeout)); - } + private void writeActorOptions(Utf8JsonWriter writer, ActorRuntimeOptions actorOptions) + { + if (actorOptions.ActorIdleTimeout != null) + { + writer.WriteString("actorIdleTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.ActorIdleTimeout)); + } - // default is false, don't write it if default - if (actorOptions.DrainRebalancedActors != false) - { - writer.WriteBoolean("drainRebalancedActors", (actorOptions.DrainRebalancedActors)); - } + if (actorOptions.ActorScanInterval != null) + { + writer.WriteString("actorScanInterval", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.ActorScanInterval)); + } - // default is null, don't write it if default - if (actorOptions.RemindersStoragePartitions != null) - { - writer.WriteNumber("remindersStoragePartitions", actorOptions.RemindersStoragePartitions.Value); - } + if (actorOptions.DrainOngoingCallTimeout != null) + { + writer.WriteString("drainOngoingCallTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.DrainOngoingCallTimeout)); + } - // Reentrancy has a default value so it is always included. - writer.WriteStartObject("reentrancy"); - writer.WriteBoolean("enabled", actorOptions.ReentrancyConfig.Enabled); - if (actorOptions.ReentrancyConfig.MaxStackDepth != null) - { - writer.WriteNumber("maxStackDepth", actorOptions.ReentrancyConfig.MaxStackDepth.Value); - } - writer.WriteEndObject(); + // default is false, don't write it if default + if (actorOptions.DrainRebalancedActors != false) + { + writer.WriteBoolean("drainRebalancedActors", (actorOptions.DrainRebalancedActors)); } - // Deactivates an actor for an actor type with given actor id. - internal async Task DeactivateAsync(string actorTypeName, string actorId) + // default is null, don't write it if default + if (actorOptions.RemindersStoragePartitions != null) { - using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}", actorTypeName, actorId)) - { - await GetActorManager(actorTypeName).DeactivateActorAsync(new ActorId(actorId)); - } + writer.WriteNumber("remindersStoragePartitions", actorOptions.RemindersStoragePartitions.Value); } - // Invokes the specified method for the actor when used with Remoting from CSharp client. - internal Task> DispatchWithRemotingAsync(string actorTypeName, string actorId, string actorMethodName, string daprActorheader, Stream data, CancellationToken cancellationToken = default) + // Reentrancy has a default value so it is always included. + writer.WriteStartObject("reentrancy"); + writer.WriteBoolean("enabled", actorOptions.ReentrancyConfig.Enabled); + if (actorOptions.ReentrancyConfig.MaxStackDepth != null) { - using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, MethodName: {Reminder}", actorTypeName, actorId, actorMethodName)) - { - return GetActorManager(actorTypeName).DispatchWithRemotingAsync(new ActorId(actorId), actorMethodName, daprActorheader, data, cancellationToken); - } + writer.WriteNumber("maxStackDepth", actorOptions.ReentrancyConfig.MaxStackDepth.Value); } + writer.WriteEndObject(); + } - // Invokes the specified method for the actor when used without remoting, this is mainly used for cross language invocation. - internal Task DispatchWithoutRemotingAsync(string actorTypeName, string actorId, string actorMethodName, Stream requestBodyStream, Stream responseBodyStream, CancellationToken cancellationToken = default) + // Deactivates an actor for an actor type with given actor id. + internal async Task DeactivateAsync(string actorTypeName, string actorId) + { + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}", actorTypeName, actorId)) { - return GetActorManager(actorTypeName).DispatchWithoutRemotingAsync(new ActorId(actorId), actorMethodName, requestBodyStream, responseBodyStream, cancellationToken); + await GetActorManager(actorTypeName).DeactivateActorAsync(new ActorId(actorId)); } + } - // Fires a reminder for the Actor. - internal Task FireReminderAsync(string actorTypeName, string actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default) + // Invokes the specified method for the actor when used with Remoting from CSharp client. + internal Task> DispatchWithRemotingAsync(string actorTypeName, string actorId, string actorMethodName, string daprActorheader, Stream data, CancellationToken cancellationToken = default) + { + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, MethodName: {Reminder}", actorTypeName, actorId, actorMethodName)) { - using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, ReminderName: {Reminder}", actorTypeName, actorId, reminderName)) - { - return GetActorManager(actorTypeName).FireReminderAsync(new ActorId(actorId), reminderName, requestBodyStream, cancellationToken); - } + return GetActorManager(actorTypeName).DispatchWithRemotingAsync(new ActorId(actorId), actorMethodName, daprActorheader, data, cancellationToken); } + } + + // Invokes the specified method for the actor when used without remoting, this is mainly used for cross language invocation. + internal Task DispatchWithoutRemotingAsync(string actorTypeName, string actorId, string actorMethodName, Stream requestBodyStream, Stream responseBodyStream, CancellationToken cancellationToken = default) + { + return GetActorManager(actorTypeName).DispatchWithoutRemotingAsync(new ActorId(actorId), actorMethodName, requestBodyStream, responseBodyStream, cancellationToken); + } - // Fires a timer for the Actor. - internal Task FireTimerAsync(string actorTypeName, string actorId, string timerName, Stream requestBodyStream, CancellationToken cancellationToken = default) + // Fires a reminder for the Actor. + internal Task FireReminderAsync(string actorTypeName, string actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default) + { + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, ReminderName: {Reminder}", actorTypeName, actorId, reminderName)) { - using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, TimerName: {Timer}", actorTypeName, actorId, timerName)) - { - return GetActorManager(actorTypeName).FireTimerAsync(new ActorId(actorId), requestBodyStream, cancellationToken); - } + return GetActorManager(actorTypeName).FireReminderAsync(new ActorId(actorId), reminderName, requestBodyStream, cancellationToken); } + } - private ActorManager GetActorManager(string actorTypeName) + // Fires a timer for the Actor. + internal Task FireTimerAsync(string actorTypeName, string actorId, string timerName, Stream requestBodyStream, CancellationToken cancellationToken = default) + { + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, TimerName: {Timer}", actorTypeName, actorId, timerName)) { - if (!this.actorManagers.TryGetValue(actorTypeName, out var actorManager)) - { - var errorMsg = $"Actor type {actorTypeName} is not registered with Actor runtime."; - throw new InvalidOperationException(errorMsg); - } + return GetActorManager(actorTypeName).FireTimerAsync(new ActorId(actorId), requestBodyStream, cancellationToken); + } + } - return actorManager; + private ActorManager GetActorManager(string actorTypeName) + { + if (!this.actorManagers.TryGetValue(actorTypeName, out var actorManager)) + { + var errorMsg = $"Actor type {actorTypeName} is not registered with Actor runtime."; + throw new InvalidOperationException(errorMsg); } + + return actorManager; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs index 21c302018..b03cfc7d1 100644 --- a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs +++ b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs @@ -16,223 +16,222 @@ using System; using System.Text.Json; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents Dapr actor configuration for this app. Includes the registration of actor types +/// as well as runtime options. +/// +/// See https://docs.dapr.io/reference/api/actors_api/ +/// +public sealed class ActorRuntimeOptions { + private TimeSpan? actorIdleTimeout; + private TimeSpan? actorScanInterval; + private TimeSpan? drainOngoingCallTimeout; + private bool drainRebalancedActors; + private ActorReentrancyConfig reentrancyConfig = new ActorReentrancyConfig + { + Enabled = false, + }; + private bool useJsonSerialization = false; + private JsonSerializerOptions jsonSerializerOptions = JsonSerializerDefaults.Web; + private string daprApiToken = string.Empty; + private int? remindersStoragePartitions = null; + + /// + /// Gets the collection of instances. + /// + public ActorRegistrationCollection Actors { get; } = new ActorRegistrationCollection(); + /// - /// Represents Dapr actor configuration for this app. Includes the registration of actor types - /// as well as runtime options. - /// - /// See https://docs.dapr.io/reference/api/actors_api/ + /// Specifies how long to wait before deactivating an idle actor. An actor is idle + /// if no actor method calls and no reminders have fired on it. + /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code + /// for more including default values. /// - public sealed class ActorRuntimeOptions + public TimeSpan? ActorIdleTimeout { - private TimeSpan? actorIdleTimeout; - private TimeSpan? actorScanInterval; - private TimeSpan? drainOngoingCallTimeout; - private bool drainRebalancedActors; - private ActorReentrancyConfig reentrancyConfig = new ActorReentrancyConfig - { - Enabled = false, - }; - private bool useJsonSerialization = false; - private JsonSerializerOptions jsonSerializerOptions = JsonSerializerDefaults.Web; - private string daprApiToken = string.Empty; - private int? remindersStoragePartitions = null; - - /// - /// Gets the collection of instances. - /// - public ActorRegistrationCollection Actors { get; } = new ActorRegistrationCollection(); - - /// - /// Specifies how long to wait before deactivating an idle actor. An actor is idle - /// if no actor method calls and no reminders have fired on it. - /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code - /// for more including default values. - /// - public TimeSpan? ActorIdleTimeout - { - get + get + { + return this.actorIdleTimeout; + } + + set + { + if (value <= TimeSpan.Zero) { - return this.actorIdleTimeout; + throw new ArgumentOutOfRangeException(nameof(actorIdleTimeout), actorIdleTimeout, "must be positive"); } - set - { - if (value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(actorIdleTimeout), actorIdleTimeout, "must be positive"); - } + this.actorIdleTimeout = value; + } + } - this.actorIdleTimeout = value; - } + /// + /// A duration which specifies how often to scan for actors to deactivate idle actors. + /// Actors that have been idle longer than the actorIdleTimeout will be deactivated. + /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code + /// for more including default values. + /// + public TimeSpan? ActorScanInterval + { + get + { + return this.actorScanInterval; } - /// - /// A duration which specifies how often to scan for actors to deactivate idle actors. - /// Actors that have been idle longer than the actorIdleTimeout will be deactivated. - /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code - /// for more including default values. - /// - public TimeSpan? ActorScanInterval + set { - get + if (value <= TimeSpan.Zero) { - return this.actorScanInterval; + throw new ArgumentOutOfRangeException(nameof(actorScanInterval), actorScanInterval, "must be positive"); } - set - { - if (value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(actorScanInterval), actorScanInterval, "must be positive"); - } + this.actorScanInterval = value; + } + } - this.actorScanInterval = value; - } + /// + /// A duration used when in the process of draining rebalanced actors. This specifies + /// how long to wait for the current active actor method to finish. If there is no current actor method call, this is ignored. + /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code + /// for more including default values. + /// + public TimeSpan? DrainOngoingCallTimeout + { + get + { + return this.drainOngoingCallTimeout; } - /// - /// A duration used when in the process of draining rebalanced actors. This specifies - /// how long to wait for the current active actor method to finish. If there is no current actor method call, this is ignored. - /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code - /// for more including default values. - /// - public TimeSpan? DrainOngoingCallTimeout + set { - get + if (value <= TimeSpan.Zero) { - return this.drainOngoingCallTimeout; + throw new ArgumentOutOfRangeException(nameof(drainOngoingCallTimeout), drainOngoingCallTimeout, "must be positive"); } - set - { - if (value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(drainOngoingCallTimeout), drainOngoingCallTimeout, "must be positive"); - } + this.drainOngoingCallTimeout = value; + } + } - this.drainOngoingCallTimeout = value; - } + /// + /// A bool. If true, Dapr will wait for drainOngoingCallTimeout to allow a current + /// actor call to complete before trying to deactivate an actor. If false, do not wait. + /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code + /// for more including default values. + /// + public bool DrainRebalancedActors + { + get + { + return this.drainRebalancedActors; } - /// - /// A bool. If true, Dapr will wait for drainOngoingCallTimeout to allow a current - /// actor call to complete before trying to deactivate an actor. If false, do not wait. - /// See https://docs.dapr.io/reference/api/actors_api/#dapr-calling-to-user-service-code - /// for more including default values. - /// - public bool DrainRebalancedActors + set { - get - { - return this.drainRebalancedActors; - } + this.drainRebalancedActors = value; + } + } - set - { - this.drainRebalancedActors = value; - } + /// + /// A configuration that defines the Actor Reentrancy parameters. If set and enabled, Actors + /// will be able to perform reentrant calls to themselves/others. If not set or false, Actors + /// will continue to lock for every request. + /// See https://docs.dapr.io/developing-applications/building-blocks/actors/actor-reentrancy/ + /// + public ActorReentrancyConfig ReentrancyConfig + { + get + { + return this.reentrancyConfig; } - /// - /// A configuration that defines the Actor Reentrancy parameters. If set and enabled, Actors - /// will be able to perform reentrant calls to themselves/others. If not set or false, Actors - /// will continue to lock for every request. - /// See https://docs.dapr.io/developing-applications/building-blocks/actors/actor-reentrancy/ - /// - public ActorReentrancyConfig ReentrancyConfig + set { - get - { - return this.reentrancyConfig; - } + this.reentrancyConfig = value; + } + } - set - { - this.reentrancyConfig = value; - } + /// + /// Enable JSON serialization for actor proxy message serialization in both remoting and non-remoting invocations. + /// + public bool UseJsonSerialization + { + get + { + return this.useJsonSerialization; } - /// - /// Enable JSON serialization for actor proxy message serialization in both remoting and non-remoting invocations. - /// - public bool UseJsonSerialization + set { - get - { - return this.useJsonSerialization; - } + this.useJsonSerialization = value; + } + } - set - { - this.useJsonSerialization = value; - } + /// + /// The to use for actor state persistence and message deserialization + /// + public JsonSerializerOptions JsonSerializerOptions + { + get + { + return this.jsonSerializerOptions; } - /// - /// The to use for actor state persistence and message deserialization - /// - public JsonSerializerOptions JsonSerializerOptions + set { - get - { - return this.jsonSerializerOptions; - } + this.jsonSerializerOptions = value ?? throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorRuntimeOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); + } + } - set - { - this.jsonSerializerOptions = value ?? throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorRuntimeOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); - } + /// + /// The to add to the headers in requests to Dapr runtime + /// + public string? DaprApiToken + { + get + { + return this.daprApiToken; } - /// - /// The to add to the headers in requests to Dapr runtime - /// - public string? DaprApiToken + set { - get - { - return this.daprApiToken; - } + this.daprApiToken = value ?? throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); + } + } - set - { - this.daprApiToken = value ?? throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); - } + /// + /// An int used to determine how many partitions to use for reminders storage. + /// + public int? RemindersStoragePartitions + { + get + { + return this.remindersStoragePartitions; } - /// - /// An int used to determine how many partitions to use for reminders storage. - /// - public int? RemindersStoragePartitions + set { - get + if (value < 0) { - return this.remindersStoragePartitions; + throw new ArgumentOutOfRangeException(nameof(remindersStoragePartitions), remindersStoragePartitions, "must be positive"); } - set - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(remindersStoragePartitions), remindersStoragePartitions, "must be positive"); - } - - this.remindersStoragePartitions = value; - } + this.remindersStoragePartitions = value; } - - /// - /// Gets or sets the HTTP endpoint URI used to communicate with the Dapr sidecar. - /// - /// - /// The URI endpoint to use for HTTP calls to the Dapr runtime. The default value will be - /// DAPR_HTTP_ENDPOINT first, or http://127.0.0.1:DAPR_HTTP_PORT as fallback - /// where DAPR_HTTP_ENDPOINT and DAPR_HTTP_PORT represents the value of the - /// corresponding environment variables. - /// - /// - public string? HttpEndpoint { get; set; } } -} + + /// + /// Gets or sets the HTTP endpoint URI used to communicate with the Dapr sidecar. + /// + /// + /// The URI endpoint to use for HTTP calls to the Dapr runtime. The default value will be + /// DAPR_HTTP_ENDPOINT first, or http://127.0.0.1:DAPR_HTTP_PORT as fallback + /// where DAPR_HTTP_ENDPOINT and DAPR_HTTP_PORT represents the value of the + /// corresponding environment variables. + /// + /// + public string? HttpEndpoint { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorStateChange.cs b/src/Dapr.Actors/Runtime/ActorStateChange.cs index 34fa68fdf..0935f679e 100644 --- a/src/Dapr.Actors/Runtime/ActorStateChange.cs +++ b/src/Dapr.Actors/Runtime/ActorStateChange.cs @@ -11,75 +11,74 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime -{ - using System; +namespace Dapr.Actors.Runtime; + +using System; +/// +/// Represents a change to an actor state with a given state name. +/// +public sealed class ActorStateChange +{ /// - /// Represents a change to an actor state with a given state name. + /// Initializes a new instance of the class. /// - public sealed class ActorStateChange + /// The name of the actor state. + /// The type of value associated with given actor state name. + /// The value associated with given actor state name. + /// The kind of state change for given actor state name. + /// The time to live for the state. + public ActorStateChange(string stateName, Type type, object value, StateChangeKind changeKind, DateTimeOffset? ttlExpireTime) { - /// - /// Initializes a new instance of the class. - /// - /// The name of the actor state. - /// The type of value associated with given actor state name. - /// The value associated with given actor state name. - /// The kind of state change for given actor state name. - /// The time to live for the state. - public ActorStateChange(string stateName, Type type, object value, StateChangeKind changeKind, DateTimeOffset? ttlExpireTime) - { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - this.StateName = stateName; - this.Type = type; - this.Value = value; - this.ChangeKind = changeKind; - this.TTLExpireTime = ttlExpireTime; - } + this.StateName = stateName; + this.Type = type; + this.Value = value; + this.ChangeKind = changeKind; + this.TTLExpireTime = ttlExpireTime; + } - /// - /// Gets the name of the actor state. - /// - /// - /// The name of the actor state. - /// - public string StateName { get; } + /// + /// Gets the name of the actor state. + /// + /// + /// The name of the actor state. + /// + public string StateName { get; } - /// - /// Gets the type of value associated with given actor state name. - /// - /// - /// The type of value associated with given actor state name. - /// - public Type Type { get; } + /// + /// Gets the type of value associated with given actor state name. + /// + /// + /// The type of value associated with given actor state name. + /// + public Type Type { get; } - /// - /// Gets the value associated with given actor state name. - /// - /// - /// The value associated with given actor state name. - /// - public object Value { get; } + /// + /// Gets the value associated with given actor state name. + /// + /// + /// The value associated with given actor state name. + /// + public object Value { get; } - /// - /// Gets the kind of state change for given actor state name. - /// - /// - /// The kind of state change for given actor state name. - /// - public StateChangeKind ChangeKind { get; } + /// + /// Gets the kind of state change for given actor state name. + /// + /// + /// The kind of state change for given actor state name. + /// + public StateChangeKind ChangeKind { get; } - /// - /// Gets the time to live for the state. - /// - /// - /// The time to live for the state. - /// - /// - /// If null, the state will not expire. - /// - public DateTimeOffset? TTLExpireTime { get; } - } -} + /// + /// Gets the time to live for the state. + /// + /// + /// The time to live for the state. + /// + /// + /// If null, the state will not expire. + /// + public DateTimeOffset? TTLExpireTime { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorStateManager.cs b/src/Dapr.Actors/Runtime/ActorStateManager.cs index 31ada4433..277f6c4c8 100644 --- a/src/Dapr.Actors/Runtime/ActorStateManager.cs +++ b/src/Dapr.Actors/Runtime/ActorStateManager.cs @@ -19,567 +19,566 @@ using Dapr.Actors.Resources; using Dapr.Actors.Communication; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +internal sealed class ActorStateManager : IActorStateManager, IActorContextualState { - internal sealed class ActorStateManager : IActorStateManager, IActorContextualState + private readonly Actor actor; + private readonly string actorTypeName; + private readonly Dictionary defaultTracker; + private static AsyncLocal<(string id, Dictionary tracker)> context = new AsyncLocal<(string, Dictionary)>(); + + internal ActorStateManager(Actor actor) { - private readonly Actor actor; - private readonly string actorTypeName; - private readonly Dictionary defaultTracker; - private static AsyncLocal<(string id, Dictionary tracker)> context = new AsyncLocal<(string, Dictionary)>(); + this.actor = actor; + this.actorTypeName = actor.Host.ActorTypeInfo.ActorTypeName; + this.defaultTracker = new Dictionary(); + } - internal ActorStateManager(Actor actor) - { - this.actor = actor; - this.actorTypeName = actor.Host.ActorTypeInfo.ActorTypeName; - this.defaultTracker = new Dictionary(); - } + public async Task AddStateAsync(string stateName, T value, CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); - public async Task AddStateAsync(string stateName, T value, CancellationToken cancellationToken) + if (!(await this.TryAddStateAsync(stateName, value, cancellationToken))) { - EnsureStateProviderInitialized(); - - if (!(await this.TryAddStateAsync(stateName, value, cancellationToken))) - { - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ActorStateAlreadyExists, stateName)); - } + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ActorStateAlreadyExists, stateName)); } + } - public async Task AddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken) - { - EnsureStateProviderInitialized(); - - if (!(await this.TryAddStateAsync(stateName, value, ttl, cancellationToken))) - { - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ActorStateAlreadyExists, stateName)); - } - } + public async Task AddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); - public async Task TryAddStateAsync(string stateName, T value, CancellationToken cancellationToken = default) + if (!(await this.TryAddStateAsync(stateName, value, ttl, cancellationToken))) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - - EnsureStateProviderInitialized(); + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ActorStateAlreadyExists, stateName)); + } + } - var stateChangeTracker = GetContextualStateTracker(); + public async Task TryAddStateAsync(string stateName, T value, CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; + EnsureStateProviderInitialized(); - // Check if the property was marked as remove or is expired in the cache - if (stateMetadata.ChangeKind == StateChangeKind.Remove || (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow)) - { - stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Update); - return true; - } + var stateChangeTracker = GetContextualStateTracker(); - return false; - } + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; - if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) + // Check if the property was marked as remove or is expired in the cache + if (stateMetadata.ChangeKind == StateChangeKind.Remove || (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow)) { - return false; + stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Update); + return true; } - stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add); - return true; + return false; } - public async Task TryAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default) + if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + return false; + } - EnsureStateProviderInitialized(); + stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add); + return true; + } - var stateChangeTracker = GetContextualStateTracker(); + public async Task TryAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; + EnsureStateProviderInitialized(); - // Check if the property was marked as remove in the cache or has been expired. - if (stateMetadata.ChangeKind == StateChangeKind.Remove || (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow)) - { - stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Update, ttl: ttl); - return true; - } + var stateChangeTracker = GetContextualStateTracker(); - return false; - } + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; - if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) + // Check if the property was marked as remove in the cache or has been expired. + if (stateMetadata.ChangeKind == StateChangeKind.Remove || (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow)) { - return false; + stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Update, ttl: ttl); + return true; } - stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add, ttl: ttl); - return true; + return false; } - public async Task GetStateAsync(string stateName, CancellationToken cancellationToken) + if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) { - EnsureStateProviderInitialized(); + return false; + } - var condRes = await this.TryGetStateAsync(stateName, cancellationToken); + stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add, ttl: ttl); + return true; + } - if (condRes.HasValue) - { - return condRes.Value; - } + public async Task GetStateAsync(string stateName, CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); - throw new KeyNotFoundException(string.Format(CultureInfo.CurrentCulture, SR.ErrorNamedActorStateNotFound, stateName)); - } + var condRes = await this.TryGetStateAsync(stateName, cancellationToken); - public async Task> TryGetStateAsync(string stateName, CancellationToken cancellationToken) + if (condRes.HasValue) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + return condRes.Value; + } - EnsureStateProviderInitialized(); + throw new KeyNotFoundException(string.Format(CultureInfo.CurrentCulture, SR.ErrorNamedActorStateNotFound, stateName)); + } - var stateChangeTracker = GetContextualStateTracker(); + public async Task> TryGetStateAsync(string stateName, CancellationToken cancellationToken) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; + EnsureStateProviderInitialized(); - // Check if the property was marked as remove in the cache or is expired - if (stateMetadata.ChangeKind == StateChangeKind.Remove || (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow)) - { - return new ConditionalValue(false, default); - } + var stateChangeTracker = GetContextualStateTracker(); - return new ConditionalValue(true, (T)stateMetadata.Value); - } + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; - var conditionalResult = await this.TryGetStateFromStateProviderAsync(stateName, cancellationToken); - if (conditionalResult.HasValue) + // Check if the property was marked as remove in the cache or is expired + if (stateMetadata.ChangeKind == StateChangeKind.Remove || (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow)) { - stateChangeTracker.Add(stateName, StateMetadata.Create(conditionalResult.Value.Value, StateChangeKind.None, ttlExpireTime: conditionalResult.Value.TTLExpireTime)); - return new ConditionalValue(true, conditionalResult.Value.Value); + return new ConditionalValue(false, default); } - return new ConditionalValue(false, default); + return new ConditionalValue(true, (T)stateMetadata.Value); } - public async Task SetStateAsync(string stateName, T value, CancellationToken cancellationToken) + var conditionalResult = await this.TryGetStateFromStateProviderAsync(stateName, cancellationToken); + if (conditionalResult.HasValue) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + stateChangeTracker.Add(stateName, StateMetadata.Create(conditionalResult.Value.Value, StateChangeKind.None, ttlExpireTime: conditionalResult.Value.TTLExpireTime)); + return new ConditionalValue(true, conditionalResult.Value.Value); + } - EnsureStateProviderInitialized(); + return new ConditionalValue(false, default); + } - var stateChangeTracker = GetContextualStateTracker(); + public async Task SetStateAsync(string stateName, T value, CancellationToken cancellationToken) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; - stateMetadata.Value = value; - stateMetadata.TTLExpireTime = null; + EnsureStateProviderInitialized(); - if (stateMetadata.ChangeKind == StateChangeKind.None || - stateMetadata.ChangeKind == StateChangeKind.Remove) - { - stateMetadata.ChangeKind = StateChangeKind.Update; - } - } - else if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) - { - stateChangeTracker.Add(stateName, StateMetadata.Create(value, StateChangeKind.Update)); - } - else + var stateChangeTracker = GetContextualStateTracker(); + + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; + stateMetadata.Value = value; + stateMetadata.TTLExpireTime = null; + + if (stateMetadata.ChangeKind == StateChangeKind.None || + stateMetadata.ChangeKind == StateChangeKind.Remove) { - stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add); + stateMetadata.ChangeKind = StateChangeKind.Update; } } - - public async Task SetStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken) + else if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - - EnsureStateProviderInitialized(); + stateChangeTracker.Add(stateName, StateMetadata.Create(value, StateChangeKind.Update)); + } + else + { + stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add); + } + } - var stateChangeTracker = GetContextualStateTracker(); + public async Task SetStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; - stateMetadata.Value = value; - stateMetadata.TTLExpireTime = DateTimeOffset.UtcNow.Add(ttl); + EnsureStateProviderInitialized(); - if (stateMetadata.ChangeKind == StateChangeKind.None || - stateMetadata.ChangeKind == StateChangeKind.Remove) - { - stateMetadata.ChangeKind = StateChangeKind.Update; - } - } - else if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) - { - stateChangeTracker.Add(stateName, StateMetadata.Create(value, StateChangeKind.Update, ttl: ttl)); - } - else - { - stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add, ttl: ttl); - } - } + var stateChangeTracker = GetContextualStateTracker(); - public async Task RemoveStateAsync(string stateName, CancellationToken cancellationToken) + if (stateChangeTracker.ContainsKey(stateName)) { - EnsureStateProviderInitialized(); + var stateMetadata = stateChangeTracker[stateName]; + stateMetadata.Value = value; + stateMetadata.TTLExpireTime = DateTimeOffset.UtcNow.Add(ttl); - if (!(await this.TryRemoveStateAsync(stateName, cancellationToken))) + if (stateMetadata.ChangeKind == StateChangeKind.None || + stateMetadata.ChangeKind == StateChangeKind.Remove) { - throw new KeyNotFoundException(string.Format(CultureInfo.CurrentCulture, SR.ErrorNamedActorStateNotFound, stateName)); + stateMetadata.ChangeKind = StateChangeKind.Update; } } - - public async Task TryRemoveStateAsync(string stateName, CancellationToken cancellationToken) + else if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) + { + stateChangeTracker.Add(stateName, StateMetadata.Create(value, StateChangeKind.Update, ttl: ttl)); + } + else { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + stateChangeTracker[stateName] = StateMetadata.Create(value, StateChangeKind.Add, ttl: ttl); + } + } - EnsureStateProviderInitialized(); + public async Task RemoveStateAsync(string stateName, CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); - var stateChangeTracker = GetContextualStateTracker(); + if (!(await this.TryRemoveStateAsync(stateName, cancellationToken))) + { + throw new KeyNotFoundException(string.Format(CultureInfo.CurrentCulture, SR.ErrorNamedActorStateNotFound, stateName)); + } + } - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; + public async Task TryRemoveStateAsync(string stateName, CancellationToken cancellationToken) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - if (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow) - { - stateChangeTracker.Remove(stateName); - return false; - } + EnsureStateProviderInitialized(); - switch (stateMetadata.ChangeKind) - { - case StateChangeKind.Remove: - return false; - case StateChangeKind.Add: - stateChangeTracker.Remove(stateName); - return true; - } + var stateChangeTracker = GetContextualStateTracker(); - stateMetadata.ChangeKind = StateChangeKind.Remove; - return true; + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; + + if (stateMetadata.TTLExpireTime.HasValue && stateMetadata.TTLExpireTime.Value <= DateTimeOffset.UtcNow) + { + stateChangeTracker.Remove(stateName); + return false; } - if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) + switch (stateMetadata.ChangeKind) { - stateChangeTracker.Add(stateName, StateMetadata.CreateForRemove()); - return true; + case StateChangeKind.Remove: + return false; + case StateChangeKind.Add: + stateChangeTracker.Remove(stateName); + return true; } - return false; + stateMetadata.ChangeKind = StateChangeKind.Remove; + return true; } - public async Task ContainsStateAsync(string stateName, CancellationToken cancellationToken) + if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + stateChangeTracker.Add(stateName, StateMetadata.CreateForRemove()); + return true; + } - EnsureStateProviderInitialized(); + return false; + } - var stateChangeTracker = GetContextualStateTracker(); + public async Task ContainsStateAsync(string stateName, CancellationToken cancellationToken) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; + EnsureStateProviderInitialized(); - // Check if the property was marked as remove in the cache - return stateMetadata.ChangeKind != StateChangeKind.Remove; - } + var stateChangeTracker = GetContextualStateTracker(); - if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) - { - return true; - } + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; - return false; + // Check if the property was marked as remove in the cache + return stateMetadata.ChangeKind != StateChangeKind.Remove; } - public async Task GetOrAddStateAsync(string stateName, T value, CancellationToken cancellationToken) + if (await this.actor.Host.StateProvider.ContainsStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken)) { - EnsureStateProviderInitialized(); - - var condRes = await this.TryGetStateAsync(stateName, cancellationToken); + return true; + } - if (condRes.HasValue) - { - return condRes.Value; - } + return false; + } - var changeKind = this.IsStateMarkedForRemove(stateName) ? StateChangeKind.Update : StateChangeKind.Add; + public async Task GetOrAddStateAsync(string stateName, T value, CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); - var stateChangeTracker = GetContextualStateTracker(); - stateChangeTracker[stateName] = StateMetadata.Create(value, changeKind); - return value; - } + var condRes = await this.TryGetStateAsync(stateName, cancellationToken); - public async Task GetOrAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken) + if (condRes.HasValue) { - EnsureStateProviderInitialized(); + return condRes.Value; + } - var condRes = await this.TryGetStateAsync(stateName, cancellationToken); + var changeKind = this.IsStateMarkedForRemove(stateName) ? StateChangeKind.Update : StateChangeKind.Add; - if (condRes.HasValue) - { - return condRes.Value; - } + var stateChangeTracker = GetContextualStateTracker(); + stateChangeTracker[stateName] = StateMetadata.Create(value, changeKind); + return value; + } - var changeKind = this.IsStateMarkedForRemove(stateName) ? StateChangeKind.Update : StateChangeKind.Add; + public async Task GetOrAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); - var stateChangeTracker = GetContextualStateTracker(); - stateChangeTracker[stateName] = StateMetadata.Create(value, changeKind, ttl: ttl); - return value; - } + var condRes = await this.TryGetStateAsync(stateName, cancellationToken); - public async Task AddOrUpdateStateAsync( - string stateName, - T addValue, - Func updateValueFactory, - CancellationToken cancellationToken = default) + if (condRes.HasValue) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + return condRes.Value; + } - EnsureStateProviderInitialized(); + var changeKind = this.IsStateMarkedForRemove(stateName) ? StateChangeKind.Update : StateChangeKind.Add; - var stateChangeTracker = GetContextualStateTracker(); + var stateChangeTracker = GetContextualStateTracker(); + stateChangeTracker[stateName] = StateMetadata.Create(value, changeKind, ttl: ttl); + return value; + } - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; + public async Task AddOrUpdateStateAsync( + string stateName, + T addValue, + Func updateValueFactory, + CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - // Check if the property was marked as remove in the cache - if (stateMetadata.ChangeKind == StateChangeKind.Remove) - { - stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Update); - return addValue; - } + EnsureStateProviderInitialized(); - var newValue = updateValueFactory.Invoke(stateName, (T)stateMetadata.Value); - stateMetadata.Value = newValue; + var stateChangeTracker = GetContextualStateTracker(); - if (stateMetadata.ChangeKind == StateChangeKind.None) - { - stateMetadata.ChangeKind = StateChangeKind.Update; - } + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; - return newValue; + // Check if the property was marked as remove in the cache + if (stateMetadata.ChangeKind == StateChangeKind.Remove) + { + stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Update); + return addValue; } - var conditionalResult = await this.TryGetStateFromStateProviderAsync(stateName, cancellationToken); - if (conditionalResult.HasValue) - { - var newValue = updateValueFactory.Invoke(stateName, conditionalResult.Value.Value); - stateChangeTracker.Add(stateName, StateMetadata.Create(newValue, StateChangeKind.Update)); + var newValue = updateValueFactory.Invoke(stateName, (T)stateMetadata.Value); + stateMetadata.Value = newValue; - return newValue; + if (stateMetadata.ChangeKind == StateChangeKind.None) + { + stateMetadata.ChangeKind = StateChangeKind.Update; } - stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Add); - return addValue; + return newValue; } - public async Task AddOrUpdateStateAsync( - string stateName, - T addValue, - Func updateValueFactory, - TimeSpan ttl, - CancellationToken cancellationToken = default) + var conditionalResult = await this.TryGetStateFromStateProviderAsync(stateName, cancellationToken); + if (conditionalResult.HasValue) { - ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); + var newValue = updateValueFactory.Invoke(stateName, conditionalResult.Value.Value); + stateChangeTracker.Add(stateName, StateMetadata.Create(newValue, StateChangeKind.Update)); - EnsureStateProviderInitialized(); + return newValue; + } - var stateChangeTracker = GetContextualStateTracker(); + stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Add); + return addValue; + } - if (stateChangeTracker.ContainsKey(stateName)) - { - var stateMetadata = stateChangeTracker[stateName]; + public async Task AddOrUpdateStateAsync( + string stateName, + T addValue, + Func updateValueFactory, + TimeSpan ttl, + CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName)); - // Check if the property was marked as remove in the cache - if (stateMetadata.ChangeKind == StateChangeKind.Remove) - { - stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Update, ttl: ttl); - return addValue; - } + EnsureStateProviderInitialized(); - var newValue = updateValueFactory.Invoke(stateName, (T)stateMetadata.Value); - stateMetadata.Value = newValue; + var stateChangeTracker = GetContextualStateTracker(); - if (stateMetadata.ChangeKind == StateChangeKind.None) - { - stateMetadata.ChangeKind = StateChangeKind.Update; - } + if (stateChangeTracker.ContainsKey(stateName)) + { + var stateMetadata = stateChangeTracker[stateName]; - return newValue; + // Check if the property was marked as remove in the cache + if (stateMetadata.ChangeKind == StateChangeKind.Remove) + { + stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Update, ttl: ttl); + return addValue; } - var conditionalResult = await this.TryGetStateFromStateProviderAsync(stateName, cancellationToken); - if (conditionalResult.HasValue) - { - var newValue = updateValueFactory.Invoke(stateName, conditionalResult.Value.Value); - stateChangeTracker.Add(stateName, StateMetadata.Create(newValue, StateChangeKind.Update, ttl: ttl)); + var newValue = updateValueFactory.Invoke(stateName, (T)stateMetadata.Value); + stateMetadata.Value = newValue; - return newValue; + if (stateMetadata.ChangeKind == StateChangeKind.None) + { + stateMetadata.ChangeKind = StateChangeKind.Update; } - stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Add, ttl: ttl); - return addValue; + return newValue; } - public Task ClearCacheAsync(CancellationToken cancellationToken) + var conditionalResult = await this.TryGetStateFromStateProviderAsync(stateName, cancellationToken); + if (conditionalResult.HasValue) { - EnsureStateProviderInitialized(); - - var stateChangeTracker = GetContextualStateTracker(); + var newValue = updateValueFactory.Invoke(stateName, conditionalResult.Value.Value); + stateChangeTracker.Add(stateName, StateMetadata.Create(newValue, StateChangeKind.Update, ttl: ttl)); - stateChangeTracker.Clear(); - return Task.CompletedTask; + return newValue; } - public async Task SaveStateAsync(CancellationToken cancellationToken = default) - { - EnsureStateProviderInitialized(); + stateChangeTracker[stateName] = StateMetadata.Create(addValue, StateChangeKind.Add, ttl: ttl); + return addValue; + } - var stateChangeTracker = GetContextualStateTracker(); + public Task ClearCacheAsync(CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); - if (stateChangeTracker.Count > 0) - { - var stateChangeList = new List(); - var statesToRemove = new List(); + var stateChangeTracker = GetContextualStateTracker(); - foreach (var stateName in stateChangeTracker.Keys) - { - var stateMetadata = stateChangeTracker[stateName]; + stateChangeTracker.Clear(); + return Task.CompletedTask; + } - if (stateMetadata.ChangeKind != StateChangeKind.None) - { - stateChangeList.Add( - new ActorStateChange(stateName, stateMetadata.Type, stateMetadata.Value, stateMetadata.ChangeKind, stateMetadata.TTLExpireTime)); + public async Task SaveStateAsync(CancellationToken cancellationToken = default) + { + EnsureStateProviderInitialized(); - if (stateMetadata.ChangeKind == StateChangeKind.Remove) - { - statesToRemove.Add(stateName); - } + var stateChangeTracker = GetContextualStateTracker(); - // Mark the states as unmodified so that tracking for next invocation is done correctly. - stateMetadata.ChangeKind = StateChangeKind.None; - } - } + if (stateChangeTracker.Count > 0) + { + var stateChangeList = new List(); + var statesToRemove = new List(); - if (stateChangeList.Count > 0) - { - await this.actor.Host.StateProvider.SaveStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateChangeList.AsReadOnly(), cancellationToken); - } + foreach (var stateName in stateChangeTracker.Keys) + { + var stateMetadata = stateChangeTracker[stateName]; - // Remove the states from tracker whcih were marked for removal. - foreach (var stateToRemove in statesToRemove) + if (stateMetadata.ChangeKind != StateChangeKind.None) { - stateChangeTracker.Remove(stateToRemove); + stateChangeList.Add( + new ActorStateChange(stateName, stateMetadata.Type, stateMetadata.Value, stateMetadata.ChangeKind, stateMetadata.TTLExpireTime)); + + if (stateMetadata.ChangeKind == StateChangeKind.Remove) + { + statesToRemove.Add(stateName); + } + + // Mark the states as unmodified so that tracking for next invocation is done correctly. + stateMetadata.ChangeKind = StateChangeKind.None; } } - } - public Task SetStateContext(string stateContext) - { - if (stateContext != null) + if (stateChangeList.Count > 0) { - context.Value = (stateContext, new Dictionary()); + await this.actor.Host.StateProvider.SaveStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateChangeList.AsReadOnly(), cancellationToken); } - else + + // Remove the states from tracker whcih were marked for removal. + foreach (var stateToRemove in statesToRemove) { - context.Value = (null, null); + stateChangeTracker.Remove(stateToRemove); } - - return Task.CompletedTask; } + } - private bool IsStateMarkedForRemove(string stateName) + public Task SetStateContext(string stateContext) + { + if (stateContext != null) + { + context.Value = (stateContext, new Dictionary()); + } + else { - var stateChangeTracker = GetContextualStateTracker(); + context.Value = (null, null); + } - if (stateChangeTracker.ContainsKey(stateName) && - stateChangeTracker[stateName].ChangeKind == StateChangeKind.Remove) - { - return true; - } + return Task.CompletedTask; + } - return false; - } + private bool IsStateMarkedForRemove(string stateName) + { + var stateChangeTracker = GetContextualStateTracker(); - private Task>> TryGetStateFromStateProviderAsync(string stateName, CancellationToken cancellationToken) + if (stateChangeTracker.ContainsKey(stateName) && + stateChangeTracker[stateName].ChangeKind == StateChangeKind.Remove) { - EnsureStateProviderInitialized(); - return this.actor.Host.StateProvider.TryLoadStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken); + return true; } - private void EnsureStateProviderInitialized() + return false; + } + + private Task>> TryGetStateFromStateProviderAsync(string stateName, CancellationToken cancellationToken) + { + EnsureStateProviderInitialized(); + return this.actor.Host.StateProvider.TryLoadStateAsync(this.actorTypeName, this.actor.Id.ToString(), stateName, cancellationToken); + } + + private void EnsureStateProviderInitialized() + { + if (this.actor.Host.StateProvider == null) { - if (this.actor.Host.StateProvider == null) - { - throw new InvalidOperationException( - "The actor was initialized without a state provider, and so cannot interact with state. " + - "If this is inside a unit test, replace Actor.StateProvider with a mock."); - } + throw new InvalidOperationException( + "The actor was initialized without a state provider, and so cannot interact with state. " + + "If this is inside a unit test, replace Actor.StateProvider with a mock."); } + } - private Dictionary GetContextualStateTracker() + private Dictionary GetContextualStateTracker() + { + if (context.Value.id != null) { - if (context.Value.id != null) - { - return context.Value.tracker; - } - else - { - return defaultTracker; - } + return context.Value.tracker; } + else + { + return defaultTracker; + } + } - private sealed class StateMetadata + private sealed class StateMetadata + { + private StateMetadata(object value, Type type, StateChangeKind changeKind, DateTimeOffset? ttlExpireTime = null, TimeSpan? ttl = null) { - private StateMetadata(object value, Type type, StateChangeKind changeKind, DateTimeOffset? ttlExpireTime = null, TimeSpan? ttl = null) - { - this.Value = value; - this.Type = type; - this.ChangeKind = changeKind; + this.Value = value; + this.Type = type; + this.ChangeKind = changeKind; - if (ttlExpireTime.HasValue && ttl.HasValue) { - throw new ArgumentException("Cannot specify both TTLExpireTime and TTL"); - } - if (ttl.HasValue) { - this.TTLExpireTime = DateTimeOffset.UtcNow.Add(ttl.Value); - } else { - this.TTLExpireTime = ttlExpireTime; - } + if (ttlExpireTime.HasValue && ttl.HasValue) { + throw new ArgumentException("Cannot specify both TTLExpireTime and TTL"); + } + if (ttl.HasValue) { + this.TTLExpireTime = DateTimeOffset.UtcNow.Add(ttl.Value); + } else { + this.TTLExpireTime = ttlExpireTime; } + } - public object Value { get; set; } + public object Value { get; set; } - public StateChangeKind ChangeKind { get; set; } + public StateChangeKind ChangeKind { get; set; } - public Type Type { get; } + public Type Type { get; } - public DateTimeOffset? TTLExpireTime { get; set; } + public DateTimeOffset? TTLExpireTime { get; set; } - public static StateMetadata Create(T value, StateChangeKind changeKind) - { - return new StateMetadata(value, typeof(T), changeKind); - } + public static StateMetadata Create(T value, StateChangeKind changeKind) + { + return new StateMetadata(value, typeof(T), changeKind); + } - public static StateMetadata Create(T value, StateChangeKind changeKind, DateTimeOffset? ttlExpireTime) - { - return new StateMetadata(value, typeof(T), changeKind, ttlExpireTime: ttlExpireTime); - } + public static StateMetadata Create(T value, StateChangeKind changeKind, DateTimeOffset? ttlExpireTime) + { + return new StateMetadata(value, typeof(T), changeKind, ttlExpireTime: ttlExpireTime); + } - public static StateMetadata Create(T value, StateChangeKind changeKind, TimeSpan? ttl) - { - return new StateMetadata(value, typeof(T), changeKind, ttl: ttl); - } + public static StateMetadata Create(T value, StateChangeKind changeKind, TimeSpan? ttl) + { + return new StateMetadata(value, typeof(T), changeKind, ttl: ttl); + } - public static StateMetadata CreateForRemove() - { - return new StateMetadata(null, typeof(object), StateChangeKind.Remove); - } + public static StateMetadata CreateForRemove() + { + return new StateMetadata(null, typeof(object), StateChangeKind.Remove); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorTestOptions.cs b/src/Dapr.Actors/Runtime/ActorTestOptions.cs index 47e73ce36..a5c965b70 100644 --- a/src/Dapr.Actors/Runtime/ActorTestOptions.cs +++ b/src/Dapr.Actors/Runtime/ActorTestOptions.cs @@ -18,93 +18,92 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Specifies optional settings settings for the when creating an actor +/// instance for testing. +/// +public sealed class ActorTestOptions { /// - /// Specifies optional settings settings for the when creating an actor - /// instance for testing. + /// Gets or sets the . + /// + /// + public ActorId ActorId { get; set; } = ActorId.CreateRandom(); + + /// + /// Gets or sets the . + /// + /// + public ILoggerFactory LoggerFactory { get; set; } = NullLoggerFactory.Instance; + + /// + /// Gets or sets the . + /// + public JsonSerializerOptions JsonSerializerOptions { get; set; } = new JsonSerializerOptions(JsonSerializerDefaults.Web); + + /// + /// Gets or sets the . /// - public sealed class ActorTestOptions + public IActorProxyFactory ProxyFactory { get; set; } = new InvalidProxyFactory(); + + /// + /// Gets or sets the . + /// + public ActorTimerManager TimerManager { get; set; } = new InvalidTimerManager(); + + private class InvalidProxyFactory : IActorProxyFactory + { + private static readonly string Message = + "This actor was initialized for tests without providing a replacement for the proxy factory. " + + "Provide a mock implementation of 'IProxyFactory' by setting 'ActorTestOptions.ProxyFactory'."; + + public ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null) + { + throw new NotImplementedException(Message); + } + + public TActorInterface CreateActorProxy(ActorId actorId, string actorType, ActorProxyOptions options = null) where TActorInterface : IActor + { + throw new NotImplementedException(Message); + } + + public object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null) + { + throw new NotImplementedException(Message); + } + } + + private class InvalidTimerManager : ActorTimerManager { - /// - /// Gets or sets the . - /// - /// - public ActorId ActorId { get; set; } = ActorId.CreateRandom(); - - /// - /// Gets or sets the . - /// - /// - public ILoggerFactory LoggerFactory { get; set; } = NullLoggerFactory.Instance; - - /// - /// Gets or sets the . - /// - public JsonSerializerOptions JsonSerializerOptions { get; set; } = new JsonSerializerOptions(JsonSerializerDefaults.Web); - - /// - /// Gets or sets the . - /// - public IActorProxyFactory ProxyFactory { get; set; } = new InvalidProxyFactory(); - - /// - /// Gets or sets the . - /// - public ActorTimerManager TimerManager { get; set; } = new InvalidTimerManager(); - - private class InvalidProxyFactory : IActorProxyFactory + private static readonly string Message = + "This actor was initialized for tests without providing a replacement for the timer manager. " + + "Provide a mock implementation of 'ActorTimerManager' by setting 'ActorTestOptions.TimerManager'."; + + public override Task RegisterReminderAsync(ActorReminder reminder) + { + throw new NotImplementedException(Message); + } + + public override Task RegisterTimerAsync(ActorTimer timer) + { + throw new NotImplementedException(Message); + } + + public override Task GetReminderAsync(ActorReminderToken reminder) + { + throw new NotImplementedException(Message); + } + + public override Task UnregisterReminderAsync(ActorReminderToken reminder) { - private static readonly string Message = - "This actor was initialized for tests without providing a replacement for the proxy factory. " + - "Provide a mock implementation of 'IProxyFactory' by setting 'ActorTestOptions.ProxyFactory'."; - - public ActorProxy Create(ActorId actorId, string actorType, ActorProxyOptions options = null) - { - throw new NotImplementedException(Message); - } - - public TActorInterface CreateActorProxy(ActorId actorId, string actorType, ActorProxyOptions options = null) where TActorInterface : IActor - { - throw new NotImplementedException(Message); - } - - public object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType, ActorProxyOptions options = null) - { - throw new NotImplementedException(Message); - } + throw new NotImplementedException(Message); } - private class InvalidTimerManager : ActorTimerManager + public override Task UnregisterTimerAsync(ActorTimerToken timer) { - private static readonly string Message = - "This actor was initialized for tests without providing a replacement for the timer manager. " + - "Provide a mock implementation of 'ActorTimerManager' by setting 'ActorTestOptions.TimerManager'."; - - public override Task RegisterReminderAsync(ActorReminder reminder) - { - throw new NotImplementedException(Message); - } - - public override Task RegisterTimerAsync(ActorTimer timer) - { - throw new NotImplementedException(Message); - } - - public override Task GetReminderAsync(ActorReminderToken reminder) - { - throw new NotImplementedException(Message); - } - - public override Task UnregisterReminderAsync(ActorReminderToken reminder) - { - throw new NotImplementedException(Message); - } - - public override Task UnregisterTimerAsync(ActorTimerToken timer) - { - throw new NotImplementedException(Message); - } + throw new NotImplementedException(Message); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorTimer.cs b/src/Dapr.Actors/Runtime/ActorTimer.cs index 786c05cb0..18c68b0b2 100644 --- a/src/Dapr.Actors/Runtime/ActorTimer.cs +++ b/src/Dapr.Actors/Runtime/ActorTimer.cs @@ -16,164 +16,163 @@ using System.Threading; using Dapr.Actors.Resources; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents the timer set on an Actor. +/// +public class ActorTimer : ActorTimerToken { + private static readonly TimeSpan MiniumPeriod = Timeout.InfiniteTimeSpan; + /// - /// Represents the timer set on an Actor. + /// Initializes a new instance of . /// - public class ActorTimer : ActorTimerToken + /// The actor type. + /// The actor id. + /// The timer name. + /// The name of the callback associated with the timer. + /// The state associated with the timer. + /// The timer due time. + /// The timer period. + public ActorTimer( + string actorType, + ActorId actorId, + string name, + string timerCallback, + byte[] data, + TimeSpan dueTime, + TimeSpan period) + : this(new ActorTimerOptions + { + ActorTypeName = actorType, + Id = actorId, + TimerName = name, + TimerCallback = timerCallback, + Data = data, + DueTime = dueTime, + Period = period, + Ttl = null + }) { - private static readonly TimeSpan MiniumPeriod = Timeout.InfiniteTimeSpan; - - /// - /// Initializes a new instance of . - /// - /// The actor type. - /// The actor id. - /// The timer name. - /// The name of the callback associated with the timer. - /// The state associated with the timer. - /// The timer due time. - /// The timer period. - public ActorTimer( - string actorType, - ActorId actorId, - string name, - string timerCallback, - byte[] data, - TimeSpan dueTime, - TimeSpan period) - : this(new ActorTimerOptions - { - ActorTypeName = actorType, - Id = actorId, - TimerName = name, - TimerCallback = timerCallback, - Data = data, - DueTime = dueTime, - Period = period, - Ttl = null - }) + } + + /// + /// Initializes a new instance of . + /// + /// The actor type. + /// The actor id. + /// The timer name. + /// The name of the callback associated with the timer. + /// The state associated with the timer. + /// The timer due time. + /// The timer period. + /// The timer ttl. + public ActorTimer( + string actorType, + ActorId actorId, + string name, + string timerCallback, + byte[] data, + TimeSpan dueTime, + TimeSpan period, + TimeSpan ttl) + : this(new ActorTimerOptions { - } + ActorTypeName = actorType, + Id = actorId, + TimerName = name, + TimerCallback = timerCallback, + Data = data, + DueTime = dueTime, + Period = period, + Ttl = ttl + }) + { + } - /// - /// Initializes a new instance of . - /// - /// The actor type. - /// The actor id. - /// The timer name. - /// The name of the callback associated with the timer. - /// The state associated with the timer. - /// The timer due time. - /// The timer period. - /// The timer ttl. - public ActorTimer( - string actorType, - ActorId actorId, - string name, - string timerCallback, - byte[] data, - TimeSpan dueTime, - TimeSpan period, - TimeSpan ttl) - : this(new ActorTimerOptions - { - ActorTypeName = actorType, - Id = actorId, - TimerName = name, - TimerCallback = timerCallback, - Data = data, - DueTime = dueTime, - Period = period, - Ttl = ttl - }) + /// + /// Initializes a new instance of . + /// + /// An containing the various settings for an . + internal ActorTimer(ActorTimerOptions options) : base(options.ActorTypeName, options.Id, options.TimerName) + { + if (options.DueTime < TimeSpan.Zero) { + throw new ArgumentOutOfRangeException(nameof(options.DueTime), string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + TimeSpan.Zero.TotalMilliseconds, + TimeSpan.MaxValue.TotalMilliseconds)); } - /// - /// Initializes a new instance of . - /// - /// An containing the various settings for an . - internal ActorTimer(ActorTimerOptions options) : base(options.ActorTypeName, options.Id, options.TimerName) + if (options.Period < MiniumPeriod) { - if (options.DueTime < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(options.DueTime), string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - TimeSpan.Zero.TotalMilliseconds, - TimeSpan.MaxValue.TotalMilliseconds)); - } - - if (options.Period < MiniumPeriod) - { - throw new ArgumentOutOfRangeException(nameof(options.Period), string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - MiniumPeriod.TotalMilliseconds, - TimeSpan.MaxValue.TotalMilliseconds)); - } - - if (options.Ttl != null && (options.Ttl < options.DueTime || options.Ttl < TimeSpan.Zero)) - { - throw new ArgumentOutOfRangeException(nameof(options.Ttl), string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - options.DueTime, - TimeSpan.MaxValue.TotalMilliseconds)); - } - - this.TimerCallback = options.TimerCallback; - this.Data = options.Data; - this.DueTime = options.DueTime; - this.Period = options.Period; - this.Ttl = options.Ttl; + throw new ArgumentOutOfRangeException(nameof(options.Period), string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + MiniumPeriod.TotalMilliseconds, + TimeSpan.MaxValue.TotalMilliseconds)); } - /// - /// The constructor - /// - [Obsolete("This constructor does not provide all required data and should not be used.")] - public ActorTimer( - string timerName, - TimerInfo timerInfo) - : base("", ActorId.CreateRandom(), timerName) + if (options.Ttl != null && (options.Ttl < options.DueTime || options.Ttl < TimeSpan.Zero)) { - this.TimerInfo = timerInfo; + throw new ArgumentOutOfRangeException(nameof(options.Ttl), string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + options.DueTime, + TimeSpan.MaxValue.TotalMilliseconds)); } - /// - /// Timer related information - /// - #pragma warning disable 0618 - public TimerInfo TimerInfo { get; } - #pragma warning restore 0618 - - - /// - /// Gets the callback name. - /// - public string TimerCallback { get; } - - /// - /// Gets the state passed to the callback. - /// - public byte[] Data { get; } - - /// - /// Gets the time when the timer is first due to be invoked. - /// - public TimeSpan DueTime { get; } - - /// - /// Gets the time interval at which the timer is invoked periodically. - /// - public TimeSpan Period { get; } - - /// - /// The optional that states when the reminder will expire. - /// - public TimeSpan? Ttl { get; } + this.TimerCallback = options.TimerCallback; + this.Data = options.Data; + this.DueTime = options.DueTime; + this.Period = options.Period; + this.Ttl = options.Ttl; } -} + + /// + /// The constructor + /// + [Obsolete("This constructor does not provide all required data and should not be used.")] + public ActorTimer( + string timerName, + TimerInfo timerInfo) + : base("", ActorId.CreateRandom(), timerName) + { + this.TimerInfo = timerInfo; + } + + /// + /// Timer related information + /// +#pragma warning disable 0618 + public TimerInfo TimerInfo { get; } +#pragma warning restore 0618 + + + /// + /// Gets the callback name. + /// + public string TimerCallback { get; } + + /// + /// Gets the state passed to the callback. + /// + public byte[] Data { get; } + + /// + /// Gets the time when the timer is first due to be invoked. + /// + public TimeSpan DueTime { get; } + + /// + /// Gets the time interval at which the timer is invoked periodically. + /// + public TimeSpan Period { get; } + + /// + /// The optional that states when the reminder will expire. + /// + public TimeSpan? Ttl { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorTimerManager.cs b/src/Dapr.Actors/Runtime/ActorTimerManager.cs index 784cf418e..651357939 100644 --- a/src/Dapr.Actors/Runtime/ActorTimerManager.cs +++ b/src/Dapr.Actors/Runtime/ActorTimerManager.cs @@ -13,46 +13,45 @@ using System.Threading.Tasks; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Provides timer and reminder functionality to an actor instance. +/// +public abstract class ActorTimerManager { /// - /// Provides timer and reminder functionality to an actor instance. + /// Registers the provided reminder with the runtime. /// - public abstract class ActorTimerManager - { - /// - /// Registers the provided reminder with the runtime. - /// - /// The to register. - /// A task which will complete when the operation completes. - public abstract Task RegisterReminderAsync(ActorReminder reminder); + /// The to register. + /// A task which will complete when the operation completes. + public abstract Task RegisterReminderAsync(ActorReminder reminder); - /// - /// Gets a reminder previously registered using - /// - /// The to unregister. - /// A task which will complete when the operation completes. - public abstract Task GetReminderAsync(ActorReminderToken reminder); + /// + /// Gets a reminder previously registered using + /// + /// The to unregister. + /// A task which will complete when the operation completes. + public abstract Task GetReminderAsync(ActorReminderToken reminder); - /// - /// Unregisters the provided reminder with the runtime. - /// - /// The to unregister. - /// A task which will complete when the operation completes. - public abstract Task UnregisterReminderAsync(ActorReminderToken reminder); + /// + /// Unregisters the provided reminder with the runtime. + /// + /// The to unregister. + /// A task which will complete when the operation completes. + public abstract Task UnregisterReminderAsync(ActorReminderToken reminder); - /// - /// Registers the provided timer with the runtime. - /// - /// The to register. - /// A task which will complete when the operation completes. - public abstract Task RegisterTimerAsync(ActorTimer timer); + /// + /// Registers the provided timer with the runtime. + /// + /// The to register. + /// A task which will complete when the operation completes. + public abstract Task RegisterTimerAsync(ActorTimer timer); - /// - /// Unregisters the provided timer with the runtime. - /// - /// The to unregister. - /// A task which will complete when the operation completes. - public abstract Task UnregisterTimerAsync(ActorTimerToken timer); - } -} + /// + /// Unregisters the provided timer with the runtime. + /// + /// The to unregister. + /// A task which will complete when the operation completes. + public abstract Task UnregisterTimerAsync(ActorTimerToken timer); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorTimerOptions.cs b/src/Dapr.Actors/Runtime/ActorTimerOptions.cs index 6c6746a79..05855c3ba 100644 --- a/src/Dapr.Actors/Runtime/ActorTimerOptions.cs +++ b/src/Dapr.Actors/Runtime/ActorTimerOptions.cs @@ -1,49 +1,48 @@ using System; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Collection of all the options used for creating a . +/// +internal struct ActorTimerOptions { /// - /// Collection of all the options used for creating a . - /// - internal struct ActorTimerOptions - { - /// - /// The name of the type of the Actor that the timer will fire for. - /// - public string ActorTypeName { get; set; } - - /// - /// The that the timer will fire for. - /// - public ActorId Id { get; set; } - - /// - /// The name of the timer. - /// - public string TimerName { get; set; } - - /// - /// The name of the callback for the timer. - /// - public string TimerCallback { get; set; } - - /// - /// State that is passed to the Actor when the timer fires. - /// - public byte[] Data { get; set; } - - /// - /// that determines when the timer will first fire. - /// - public TimeSpan DueTime { get; set; } - - /// - /// that determines how much time there is between the timer firing. Starts after the . - /// - public TimeSpan Period { get; set; } - - /// - /// An optional that determines when the timer will expire. - /// - public TimeSpan? Ttl { get; set; } - } -} + /// The name of the type of the Actor that the timer will fire for. + /// + public string ActorTypeName { get; set; } + + /// + /// The that the timer will fire for. + /// + public ActorId Id { get; set; } + + /// + /// The name of the timer. + /// + public string TimerName { get; set; } + + /// + /// The name of the callback for the timer. + /// + public string TimerCallback { get; set; } + + /// + /// State that is passed to the Actor when the timer fires. + /// + public byte[] Data { get; set; } + + /// + /// that determines when the timer will first fire. + /// + public TimeSpan DueTime { get; set; } + + /// + /// that determines how much time there is between the timer firing. Starts after the . + /// + public TimeSpan Period { get; set; } + + /// + /// An optional that determines when the timer will expire. + /// + public TimeSpan? Ttl { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorTimerToken.cs b/src/Dapr.Actors/Runtime/ActorTimerToken.cs index 557b8d8bc..9f7c0c033 100644 --- a/src/Dapr.Actors/Runtime/ActorTimerToken.cs +++ b/src/Dapr.Actors/Runtime/ActorTimerToken.cs @@ -13,57 +13,56 @@ using System; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents the timer set for an actor. +/// +public class ActorTimerToken { /// - /// Represents the timer set for an actor. + /// Initializes a new . /// - public class ActorTimerToken + /// The actor type. + /// The actor id. + /// The timer name. + public ActorTimerToken( + string actorType, + ActorId actorId, + string name) { - /// - /// Initializes a new . - /// - /// The actor type. - /// The actor id. - /// The timer name. - public ActorTimerToken( - string actorType, - ActorId actorId, - string name) + if (actorType == null) { - if (actorType == null) - { - throw new ArgumentNullException(nameof(actorType)); - } - - if (actorId == null) - { - throw new ArgumentNullException(nameof(actorId)); - } + throw new ArgumentNullException(nameof(actorType)); + } - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } + if (actorId == null) + { + throw new ArgumentNullException(nameof(actorId)); + } - this.ActorType = actorType; - this.ActorId = actorId; - this.Name = name; + if (name == null) + { + throw new ArgumentNullException(nameof(name)); } - /// - /// Gets the actor type. - /// - public string ActorType { get; } + this.ActorType = actorType; + this.ActorId = actorId; + this.Name = name; + } - /// - /// Gets the actor id. - /// - public ActorId ActorId { get; } + /// + /// Gets the actor type. + /// + public string ActorType { get; } - /// - /// Gets the timer name. - /// - public string Name { get; } - } -} + /// + /// Gets the actor id. + /// + public ActorId ActorId { get; } + + /// + /// Gets the timer name. + /// + public string Name { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs b/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs index 4acaa3912..dd8d096e9 100644 --- a/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs +++ b/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs @@ -11,94 +11,93 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; +namespace Dapr.Actors.Runtime; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +/// +/// Contains extension method for Actor types. +/// +internal static class ActorTypeExtensions +{ /// - /// Contains extension method for Actor types. + /// Gets the actor interfaces implemented by the actor class. /// - internal static class ActorTypeExtensions + /// The type of class implementing actor. + /// An array containing actor interface which the type implements. + public static Type[] GetActorInterfaces(this Type type) { - /// - /// Gets the actor interfaces implemented by the actor class. - /// - /// The type of class implementing actor. - /// An array containing actor interface which the type implements. - public static Type[] GetActorInterfaces(this Type type) - { - var list = new List(type.GetInterfaces().Where(t => typeof(IActor).IsAssignableFrom(t))); - list.RemoveAll(t => (t.GetNonActorParentType() != null)); + var list = new List(type.GetInterfaces().Where(t => typeof(IActor).IsAssignableFrom(t))); + list.RemoveAll(t => (t.GetNonActorParentType() != null)); - return list.ToArray(); - } + return list.ToArray(); + } - /// - /// Indicates whether the interface type is an actor interface. - /// - /// The interface type of the actor. - /// true, if the actorInterfaceType is an interface only implements . - public static bool IsActorInterface(this Type actorInterfaceType) - { - return (actorInterfaceType.GetTypeInfo().IsInterface && (actorInterfaceType.GetNonActorParentType() == null)); - } + /// + /// Indicates whether the interface type is an actor interface. + /// + /// The interface type of the actor. + /// true, if the actorInterfaceType is an interface only implements . + public static bool IsActorInterface(this Type actorInterfaceType) + { + return (actorInterfaceType.GetTypeInfo().IsInterface && (actorInterfaceType.GetNonActorParentType() == null)); + } - /// - /// Indicates a value whether the actorType is an actor. - /// - /// The type implementing actor. - /// true, if the of actorType is an ; otherwise, false. - public static bool IsActor(this Type actorType) - { - var actorBaseType = actorType.GetTypeInfo().BaseType; + /// + /// Indicates a value whether the actorType is an actor. + /// + /// The type implementing actor. + /// true, if the of actorType is an ; otherwise, false. + public static bool IsActor(this Type actorType) + { + var actorBaseType = actorType.GetTypeInfo().BaseType; - while (actorBaseType != null) + while (actorBaseType != null) + { + if (actorBaseType == typeof(Actor)) { - if (actorBaseType == typeof(Actor)) - { - return true; - } - - actorType = actorBaseType; - actorBaseType = actorType.GetTypeInfo().BaseType; + return true; } - return false; + actorType = actorBaseType; + actorBaseType = actorType.GetTypeInfo().BaseType; } - /// - /// Indicates a value whether an actor type implements interface. - /// - /// The type implementing actor. - /// true, if the implements an interface; otherwise, false. - public static bool IsRemindableActor(this Type actorType) + return false; + } + + /// + /// Indicates a value whether an actor type implements interface. + /// + /// The type implementing actor. + /// true, if the implements an interface; otherwise, false. + public static bool IsRemindableActor(this Type actorType) + { + return actorType.IsActor() && actorType.GetInterfaces().Contains(typeof(IRemindable)); + } + + public static Type GetNonActorParentType(this Type type) + { + var list = new List(type.GetInterfaces()); + + // must have IActor as the parent, so removal of it should result in reduction in the count. + if (list.RemoveAll(t => (t == typeof(IActor))) == 0) { - return actorType.IsActor() && actorType.GetInterfaces().Contains(typeof(IRemindable)); + return type; } - public static Type GetNonActorParentType(this Type type) + foreach (var t in list) { - var list = new List(type.GetInterfaces()); - - // must have IActor as the parent, so removal of it should result in reduction in the count. - if (list.RemoveAll(t => (t == typeof(IActor))) == 0) - { - return type; - } - - foreach (var t in list) + var nonActorParent = GetNonActorParentType(t); + if (nonActorParent != null) { - var nonActorParent = GetNonActorParentType(t); - if (nonActorParent != null) - { - return nonActorParent; - } + return nonActorParent; } - - return null; } + + return null; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ActorTypeInformation.cs b/src/Dapr.Actors/Runtime/ActorTypeInformation.cs index 801524218..ffa1b5ec0 100644 --- a/src/Dapr.Actors/Runtime/ActorTypeInformation.cs +++ b/src/Dapr.Actors/Runtime/ActorTypeInformation.cs @@ -11,153 +11,152 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using Dapr.Actors.Resources; + +/// +/// Contains the information about the type implementing an actor. +/// +public sealed class ActorTypeInformation { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Reflection; - using Dapr.Actors.Resources; + /// + /// Initializes a new instance of the class. + /// + private ActorTypeInformation() + { + } + + /// + /// Gets the name of the actor type represented by the actor. + /// + /// The name of the actor type represented by the actor. + /// Defaults to the name of the class implementing the actor. Can be overridden using the property. + public string ActorTypeName { get; private set; } + + /// + /// Gets the type of the class implementing the actor. + /// + /// The of the class implementing the actor. + public Type ImplementationType { get; private set; } + + /// + /// Gets the actor interface types which derive from and implemented by actor class. + /// + /// An enumerator that can be used to iterate through the actor interface type. + public IEnumerable InterfaceTypes { get; private set; } + + /// + /// Gets a value indicating whether the class implementing actor is abstract. + /// + /// true if the class implementing actor is abstract, otherwise false. + public bool IsAbstract { get; private set; } /// - /// Contains the information about the type implementing an actor. + /// Gets a value indicating whether the actor class implements . /// - public sealed class ActorTypeInformation + /// true if the actor class implements , otherwise false. + public bool IsRemindable { get; private set; } + + /// + /// Creates the from actorType. + /// + /// The type of class implementing the actor to create ActorTypeInformation for. + /// When this method returns, contains ActorTypeInformation, if the creation of + /// ActorTypeInformation from actorType succeeded, or null if the creation failed. + /// The creation fails if the actorType parameter is null or it does not implement an actor. + /// true if ActorTypeInformation was successfully created for actorType; otherwise, false. + /// + /// Creation of ActorTypeInformation from actorType will fail when: + /// 1. for actorType is not of type . + /// 2. actorType does not implement an interface deriving from and is not marked as abstract. + /// + [Obsolete("Use Get(Type, string) instead. This will be removed in the future.")] + public static bool TryGet(Type actorType, out ActorTypeInformation actorTypeInformation) { - /// - /// Initializes a new instance of the class. - /// - private ActorTypeInformation() + try { + actorTypeInformation = Get(actorType, actorTypeName: null); + return true; } - - /// - /// Gets the name of the actor type represented by the actor. - /// - /// The name of the actor type represented by the actor. - /// Defaults to the name of the class implementing the actor. Can be overridden using the property. - public string ActorTypeName { get; private set; } - - /// - /// Gets the type of the class implementing the actor. - /// - /// The of the class implementing the actor. - public Type ImplementationType { get; private set; } - - /// - /// Gets the actor interface types which derive from and implemented by actor class. - /// - /// An enumerator that can be used to iterate through the actor interface type. - public IEnumerable InterfaceTypes { get; private set; } - - /// - /// Gets a value indicating whether the class implementing actor is abstract. - /// - /// true if the class implementing actor is abstract, otherwise false. - public bool IsAbstract { get; private set; } - - /// - /// Gets a value indicating whether the actor class implements . - /// - /// true if the actor class implements , otherwise false. - public bool IsRemindable { get; private set; } - - /// - /// Creates the from actorType. - /// - /// The type of class implementing the actor to create ActorTypeInformation for. - /// When this method returns, contains ActorTypeInformation, if the creation of - /// ActorTypeInformation from actorType succeeded, or null if the creation failed. - /// The creation fails if the actorType parameter is null or it does not implement an actor. - /// true if ActorTypeInformation was successfully created for actorType; otherwise, false. - /// - /// Creation of ActorTypeInformation from actorType will fail when: - /// 1. for actorType is not of type . - /// 2. actorType does not implement an interface deriving from and is not marked as abstract. - /// - [Obsolete("Use Get(Type, string) instead. This will be removed in the future.")] - public static bool TryGet(Type actorType, out ActorTypeInformation actorTypeInformation) + catch (ArgumentException) { - try - { - actorTypeInformation = Get(actorType, actorTypeName: null); - return true; - } - catch (ArgumentException) - { - actorTypeInformation = null; - return false; - } + actorTypeInformation = null; + return false; } + } + + /// + /// Creates an from actorType. + /// + /// The type of class implementing the actor to create ActorTypeInformation for. + /// created from actorType. + /// + /// When for actorType is not of type . + /// When actorType does not implement an interface deriving from + /// and is not marked as abstract. + /// + [Obsolete("Use Get(Type, string) instead. This will be removed in the future.")] + public static ActorTypeInformation Get(Type actorType) + { + return Get(actorType, actorTypeName: null); + } - /// - /// Creates an from actorType. - /// - /// The type of class implementing the actor to create ActorTypeInformation for. - /// created from actorType. - /// - /// When for actorType is not of type . - /// When actorType does not implement an interface deriving from - /// and is not marked as abstract. - /// - [Obsolete("Use Get(Type, string) instead. This will be removed in the future.")] - public static ActorTypeInformation Get(Type actorType) + /// + /// Creates an from actorType. + /// + /// The type of class implementing the actor to create ActorTypeInformation for. + /// The name of the actor type represented by the actor. + /// created from actorType. + /// + /// When for actorType is not of type . + /// When actorType does not implement an interface deriving from + /// and is not marked as abstract. + /// + /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . + public static ActorTypeInformation Get(Type actorType, string actorTypeName) + { + if (!actorType.IsActor()) { - return Get(actorType, actorTypeName: null); + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorNotAnActor, + actorType.FullName, + typeof(Actor).FullName), + "actorType"); } - /// - /// Creates an from actorType. - /// - /// The type of class implementing the actor to create ActorTypeInformation for. - /// The name of the actor type represented by the actor. - /// created from actorType. - /// - /// When for actorType is not of type . - /// When actorType does not implement an interface deriving from - /// and is not marked as abstract. - /// - /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . - public static ActorTypeInformation Get(Type actorType, string actorTypeName) + // get all actor interfaces + var actorInterfaces = actorType.GetActorInterfaces(); + + // ensure that the if the actor type is not abstract it implements at least one actor interface + if ((actorInterfaces.Length == 0) && (!actorType.GetTypeInfo().IsAbstract)) { - if (!actorType.IsActor()) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorNotAnActor, - actorType.FullName, - typeof(Actor).FullName), - "actorType"); - } - - // get all actor interfaces - var actorInterfaces = actorType.GetActorInterfaces(); - - // ensure that the if the actor type is not abstract it implements at least one actor interface - if ((actorInterfaces.Length == 0) && (!actorType.GetTypeInfo().IsAbstract)) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - SR.ErrorNoActorInterfaceFound, - actorType.FullName, - typeof(IActor).FullName), - "actorType"); - } - - var actorAttribute = actorType.GetCustomAttribute(); - - actorTypeName ??= actorAttribute?.TypeName ?? actorType.Name; - - return new ActorTypeInformation() - { - ActorTypeName = actorTypeName, - InterfaceTypes = actorInterfaces, - ImplementationType = actorType, - IsAbstract = actorType.GetTypeInfo().IsAbstract, - IsRemindable = actorType.IsRemindableActor(), - }; + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorNoActorInterfaceFound, + actorType.FullName, + typeof(IActor).FullName), + "actorType"); } + + var actorAttribute = actorType.GetCustomAttribute(); + + actorTypeName ??= actorAttribute?.TypeName ?? actorType.Name; + + return new ActorTypeInformation() + { + ActorTypeName = actorTypeName, + InterfaceTypes = actorInterfaces, + ImplementationType = actorType, + IsAbstract = actorType.GetTypeInfo().IsAbstract, + IsRemindable = actorType.IsRemindableActor(), + }; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/ConditionalValue.cs b/src/Dapr.Actors/Runtime/ConditionalValue.cs index ec4f3a5a6..30b5c7a2d 100644 --- a/src/Dapr.Actors/Runtime/ConditionalValue.cs +++ b/src/Dapr.Actors/Runtime/ConditionalValue.cs @@ -11,35 +11,34 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Result class returned by Reliable Collections APIs that may or may not return a value. +/// +/// The type of the value returned by this . +public struct ConditionalValue { /// - /// Result class returned by Reliable Collections APIs that may or may not return a value. + /// Initializes a new instance of the struct with the given value. /// - /// The type of the value returned by this . - public struct ConditionalValue + /// Indicates whether the value is valid. + /// The value. + public ConditionalValue(bool hasValue, TValue value) { - /// - /// Initializes a new instance of the struct with the given value. - /// - /// Indicates whether the value is valid. - /// The value. - public ConditionalValue(bool hasValue, TValue value) - { - this.HasValue = hasValue; - this.Value = value; - } + this.HasValue = hasValue; + this.Value = value; + } - /// - /// Gets a value indicating whether the current object has a valid value of its underlying type. - /// - /// true: Value is valid, false otherwise. - public bool HasValue { get; } + /// + /// Gets a value indicating whether the current object has a valid value of its underlying type. + /// + /// true: Value is valid, false otherwise. + public bool HasValue { get; } - /// - /// Gets the value of the current object if it has been assigned a valid underlying value. - /// - /// The value of the object. If HasValue is false, returns the default value for type of the TValue parameter. - public TValue Value { get; } - } -} + /// + /// Gets the value of the current object if it has been assigned a valid underlying value. + /// + /// The value of the object. If HasValue is false, returns the default value for type of the TValue parameter. + public TValue Value { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/DaprStateProvider.cs b/src/Dapr.Actors/Runtime/DaprStateProvider.cs index e81308dbd..bddbaf564 100644 --- a/src/Dapr.Actors/Runtime/DaprStateProvider.cs +++ b/src/Dapr.Actors/Runtime/DaprStateProvider.cs @@ -11,171 +11,170 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Communication; + +/// +/// State Provider to interact with Dapr runtime. +/// +internal class DaprStateProvider { - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Communication; - - /// - /// State Provider to interact with Dapr runtime. - /// - internal class DaprStateProvider - { - private readonly IActorStateSerializer actorStateSerializer; - private readonly JsonSerializerOptions jsonSerializerOptions; + private readonly IActorStateSerializer actorStateSerializer; + private readonly JsonSerializerOptions jsonSerializerOptions; - private readonly IDaprInteractor daprInteractor; + private readonly IDaprInteractor daprInteractor; - public DaprStateProvider(IDaprInteractor daprInteractor, IActorStateSerializer actorStateSerializer = null) - { - this.actorStateSerializer = actorStateSerializer; - this.daprInteractor = daprInteractor; - } + public DaprStateProvider(IDaprInteractor daprInteractor, IActorStateSerializer actorStateSerializer = null) + { + this.actorStateSerializer = actorStateSerializer; + this.daprInteractor = daprInteractor; + } - public DaprStateProvider(IDaprInteractor daprInteractor, JsonSerializerOptions jsonSerializerOptions = null) - { - this.jsonSerializerOptions = jsonSerializerOptions; - this.daprInteractor = daprInteractor; - } + public DaprStateProvider(IDaprInteractor daprInteractor, JsonSerializerOptions jsonSerializerOptions = null) + { + this.jsonSerializerOptions = jsonSerializerOptions; + this.daprInteractor = daprInteractor; + } + + public async Task>> TryLoadStateAsync(string actorType, string actorId, string stateName, CancellationToken cancellationToken = default) + { + var result = new ConditionalValue>(false, default); + var response = await this.daprInteractor.GetStateAsync(actorType, actorId, stateName, cancellationToken); - public async Task>> TryLoadStateAsync(string actorType, string actorId, string stateName, CancellationToken cancellationToken = default) + if (response.Value.Length != 0 && (!response.TTLExpireTime.HasValue || response.TTLExpireTime.Value > DateTimeOffset.UtcNow)) { - var result = new ConditionalValue>(false, default); - var response = await this.daprInteractor.GetStateAsync(actorType, actorId, stateName, cancellationToken); + T typedResult; - if (response.Value.Length != 0 && (!response.TTLExpireTime.HasValue || response.TTLExpireTime.Value > DateTimeOffset.UtcNow)) + // perform default json de-serialization if custom serializer was not provided. + if (this.actorStateSerializer != null) { - T typedResult; - - // perform default json de-serialization if custom serializer was not provided. - if (this.actorStateSerializer != null) - { - var byteResult = Convert.FromBase64String(response.Value.Trim('"')); - typedResult = this.actorStateSerializer.Deserialize(byteResult); - } - else - { - typedResult = JsonSerializer.Deserialize(response.Value, jsonSerializerOptions); - } - - result = new ConditionalValue>(true, new ActorStateResponse(typedResult, response.TTLExpireTime)); + var byteResult = Convert.FromBase64String(response.Value.Trim('"')); + typedResult = this.actorStateSerializer.Deserialize(byteResult); + } + else + { + typedResult = JsonSerializer.Deserialize(response.Value, jsonSerializerOptions); } - return result; + result = new ConditionalValue>(true, new ActorStateResponse(typedResult, response.TTLExpireTime)); } - public async Task ContainsStateAsync(string actorType, string actorId, string stateName, CancellationToken cancellationToken = default) - { - var result = await this.daprInteractor.GetStateAsync(actorType, actorId, stateName, cancellationToken); - return (result.Value.Length != 0 && (!result.TTLExpireTime.HasValue || result.TTLExpireTime.Value > DateTimeOffset.UtcNow)); - } + return result; + } - public async Task SaveStateAsync(string actorType, string actorId, IReadOnlyCollection stateChanges, CancellationToken cancellationToken = default) - { - await this.DoStateChangesTransactionallyAsync(actorType, actorId, stateChanges, cancellationToken); - } + public async Task ContainsStateAsync(string actorType, string actorId, string stateName, CancellationToken cancellationToken = default) + { + var result = await this.daprInteractor.GetStateAsync(actorType, actorId, stateName, cancellationToken); + return (result.Value.Length != 0 && (!result.TTLExpireTime.HasValue || result.TTLExpireTime.Value > DateTimeOffset.UtcNow)); + } - private async Task DoStateChangesTransactionallyAsync(string actorType, string actorId, IReadOnlyCollection stateChanges, CancellationToken cancellationToken = default) - { - // Transactional state update request body: - /* - [ - { - "operation": "upsert", - "request": { - "key": "key1", - "value": "myData" - } - }, - { - "operation": "delete", - "request": { - "key": "key2" - } + public async Task SaveStateAsync(string actorType, string actorId, IReadOnlyCollection stateChanges, CancellationToken cancellationToken = default) + { + await this.DoStateChangesTransactionallyAsync(actorType, actorId, stateChanges, cancellationToken); + } + + private async Task DoStateChangesTransactionallyAsync(string actorType, string actorId, IReadOnlyCollection stateChanges, CancellationToken cancellationToken = default) + { + // Transactional state update request body: + /* + [ + { + "operation": "upsert", + "request": { + "key": "key1", + "value": "myData" } - ] - */ - using var stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - writer.WriteStartArray(); - foreach (var stateChange in stateChanges) + }, { - writer.WriteStartObject(); - var operation = this.GetDaprStateOperation(stateChange.ChangeKind); - writer.WriteString("operation", operation); - - // write the requestProperty - writer.WritePropertyName("request"); - writer.WriteStartObject(); // start object for request property - switch (stateChange.ChangeKind) - { - case StateChangeKind.Remove: - writer.WriteString("key", stateChange.StateName); - break; - case StateChangeKind.Add: - case StateChangeKind.Update: - writer.WriteString("key", stateChange.StateName); - - // perform default json serialization if custom serializer was not provided. - if (this.actorStateSerializer != null) - { - var buffer = this.actorStateSerializer.Serialize(stateChange.Type, stateChange.Value); - writer.WriteBase64String("value", buffer); - } - else - { - writer.WritePropertyName("value"); - JsonSerializer.Serialize(writer, stateChange.Value, stateChange.Type, jsonSerializerOptions); - } - - if (stateChange.TTLExpireTime.HasValue) { - var ttl = (int)Math.Ceiling((stateChange.TTLExpireTime.Value - DateTimeOffset.UtcNow).TotalSeconds); - writer.WritePropertyName("metadata"); - writer.WriteStartObject(); - writer.WriteString("ttlInSeconds", ttl.ToString()); - writer.WriteEndObject(); - } - - break; - default: - break; + "operation": "delete", + "request": { + "key": "key2" } - - writer.WriteEndObject(); // end object for request property - writer.WriteEndObject(); } - - writer.WriteEndArray(); - - await writer.FlushAsync(); - var content = Encoding.UTF8.GetString(stream.ToArray()); - await this.daprInteractor.SaveStateTransactionallyAsync(actorType, actorId, content, cancellationToken); - } - - private string GetDaprStateOperation(StateChangeKind changeKind) + ] + */ + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + writer.WriteStartArray(); + foreach (var stateChange in stateChanges) { - var operation = string.Empty; - - switch (changeKind) + writer.WriteStartObject(); + var operation = this.GetDaprStateOperation(stateChange.ChangeKind); + writer.WriteString("operation", operation); + + // write the requestProperty + writer.WritePropertyName("request"); + writer.WriteStartObject(); // start object for request property + switch (stateChange.ChangeKind) { case StateChangeKind.Remove: - operation = "delete"; + writer.WriteString("key", stateChange.StateName); break; case StateChangeKind.Add: case StateChangeKind.Update: - operation = "upsert"; + writer.WriteString("key", stateChange.StateName); + + // perform default json serialization if custom serializer was not provided. + if (this.actorStateSerializer != null) + { + var buffer = this.actorStateSerializer.Serialize(stateChange.Type, stateChange.Value); + writer.WriteBase64String("value", buffer); + } + else + { + writer.WritePropertyName("value"); + JsonSerializer.Serialize(writer, stateChange.Value, stateChange.Type, jsonSerializerOptions); + } + + if (stateChange.TTLExpireTime.HasValue) { + var ttl = (int)Math.Ceiling((stateChange.TTLExpireTime.Value - DateTimeOffset.UtcNow).TotalSeconds); + writer.WritePropertyName("metadata"); + writer.WriteStartObject(); + writer.WriteString("ttlInSeconds", ttl.ToString()); + writer.WriteEndObject(); + } + break; default: break; } - return operation; + writer.WriteEndObject(); // end object for request property + writer.WriteEndObject(); } + + writer.WriteEndArray(); + + await writer.FlushAsync(); + var content = Encoding.UTF8.GetString(stream.ToArray()); + await this.daprInteractor.SaveStateTransactionallyAsync(actorType, actorId, content, cancellationToken); + } + + private string GetDaprStateOperation(StateChangeKind changeKind) + { + var operation = string.Empty; + + switch (changeKind) + { + case StateChangeKind.Remove: + operation = "delete"; + break; + case StateChangeKind.Add: + case StateChangeKind.Update: + operation = "upsert"; + break; + default: + break; + } + + return operation; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/DefaultActorActivator.cs b/src/Dapr.Actors/Runtime/DefaultActorActivator.cs index 768299da7..f766dec1f 100644 --- a/src/Dapr.Actors/Runtime/DefaultActorActivator.cs +++ b/src/Dapr.Actors/Runtime/DefaultActorActivator.cs @@ -14,55 +14,54 @@ using System; using System.Threading.Tasks; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// A default implementation of that uses . +/// +public class DefaultActorActivator : ActorActivator { /// - /// A default implementation of that uses . + /// Creates the actor instance and returns it inside an instance of . /// - public class DefaultActorActivator : ActorActivator + /// The actor host specifying information needed for the creation of the actor. + /// + /// Asynchronously returns n instance of . The + /// instance will be provided to when the actor is ready for deletion. + /// + /// + /// Implementations should not interact with lifecycle callback methods on the type. + /// These methods will be called by the runtime. + /// + public override Task CreateAsync(ActorHost host) { - /// - /// Creates the actor instance and returns it inside an instance of . - /// - /// The actor host specifying information needed for the creation of the actor. - /// - /// Asynchronously returns n instance of . The - /// instance will be provided to when the actor is ready for deletion. - /// - /// - /// Implementations should not interact with lifecycle callback methods on the type. - /// These methods will be called by the runtime. - /// - public override Task CreateAsync(ActorHost host) + var type = host.ActorTypeInfo.ImplementationType; + var actor = (Actor)Activator.CreateInstance(type, args: new object[]{ host, }); + return Task.FromResult(new ActorActivatorState(actor)); + } + + /// + /// Deletes the actor instance and cleans up all associated resources. + /// + /// + /// The instance that was created during creation of the actor. + /// + /// + /// A task that represents the asynchronous completion of the operation. + /// + /// + /// Implementations should not interact with lifecycle callback methods on the type. + /// These methods will be called by the runtime. + /// + public async override Task DeleteAsync(ActorActivatorState state) + { + if (state.Actor is IAsyncDisposable asyncDisposable) { - var type = host.ActorTypeInfo.ImplementationType; - var actor = (Actor)Activator.CreateInstance(type, args: new object[]{ host, }); - return Task.FromResult(new ActorActivatorState(actor)); + await asyncDisposable.DisposeAsync(); } - - /// - /// Deletes the actor instance and cleans up all associated resources. - /// - /// - /// The instance that was created during creation of the actor. - /// - /// - /// A task that represents the asynchronous completion of the operation. - /// - /// - /// Implementations should not interact with lifecycle callback methods on the type. - /// These methods will be called by the runtime. - /// - public async override Task DeleteAsync(ActorActivatorState state) + else if (state.Actor is IDisposable disposable) { - if (state.Actor is IAsyncDisposable asyncDisposable) - { - await asyncDisposable.DisposeAsync(); - } - else if (state.Actor is IDisposable disposable) - { - disposable.Dispose(); - } + disposable.Dispose(); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs b/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs index 43839c64d..4991f7f68 100644 --- a/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs +++ b/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// A default implementation of that uses . +/// +public class DefaultActorActivatorFactory : ActorActivatorFactory { /// - /// A default implementation of that uses . + /// Creates the for the provided . /// - public class DefaultActorActivatorFactory : ActorActivatorFactory + /// The . + /// An . + public override ActorActivator CreateActivator(ActorTypeInformation type) { - /// - /// Creates the for the provided . - /// - /// The . - /// An . - public override ActorActivator CreateActivator(ActorTypeInformation type) - { - return new DefaultActorActivator(); - } + return new DefaultActorActivator(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs b/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs index 79f3eabcf..faf3cb59e 100644 --- a/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs +++ b/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs @@ -17,102 +17,101 @@ using System.IO; using Grpc.Core; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +internal class DefaultActorTimerManager : ActorTimerManager { - internal class DefaultActorTimerManager : ActorTimerManager + private readonly IDaprInteractor interactor; + + public DefaultActorTimerManager(IDaprInteractor interactor) { - private readonly IDaprInteractor interactor; + this.interactor = interactor; + } - public DefaultActorTimerManager(IDaprInteractor interactor) + public override async Task RegisterReminderAsync(ActorReminder reminder) + { + if (reminder == null) { - this.interactor = interactor; + throw new ArgumentNullException(nameof(reminder)); } - public override async Task RegisterReminderAsync(ActorReminder reminder) - { - if (reminder == null) - { - throw new ArgumentNullException(nameof(reminder)); - } - - var serialized = await SerializeReminderAsync(reminder); - await this.interactor.RegisterReminderAsync(reminder.ActorType, reminder.ActorId.ToString(), reminder.Name, serialized); - } + var serialized = await SerializeReminderAsync(reminder); + await this.interactor.RegisterReminderAsync(reminder.ActorType, reminder.ActorId.ToString(), reminder.Name, serialized); + } - public override async Task GetReminderAsync(ActorReminderToken token) + public override async Task GetReminderAsync(ActorReminderToken token) + { + if (token == null) { - if (token == null) - { - throw new ArgumentNullException(nameof(token)); - } - - var response = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name); - if ((int)response.StatusCode == 500) - { - return null; - } - - var responseStream = await response.Content.ReadAsStreamAsync(); - return await DeserializeReminderAsync(responseStream, token); + throw new ArgumentNullException(nameof(token)); } - - public override async Task UnregisterReminderAsync(ActorReminderToken reminder) - { - if (reminder == null) - { - throw new ArgumentNullException(nameof(reminder)); - } - await this.interactor.UnregisterReminderAsync(reminder.ActorType, reminder.ActorId.ToString(), reminder.Name); + var response = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name); + if ((int)response.StatusCode == 500) + { + return null; } - public override async Task RegisterTimerAsync(ActorTimer timer) - { - if (timer == null) - { - throw new ArgumentNullException(nameof(timer)); - } + var responseStream = await response.Content.ReadAsStreamAsync(); + return await DeserializeReminderAsync(responseStream, token); + } - #pragma warning disable 0618 - var timerInfo = new TimerInfo(timer.TimerCallback, timer.Data, timer.DueTime, timer.Period, timer.Ttl); - #pragma warning restore 0618 - var data = JsonSerializer.Serialize(timerInfo); - await this.interactor.RegisterTimerAsync(timer.ActorType, timer.ActorId.ToString(), timer.Name, data); + public override async Task UnregisterReminderAsync(ActorReminderToken reminder) + { + if (reminder == null) + { + throw new ArgumentNullException(nameof(reminder)); } + + await this.interactor.UnregisterReminderAsync(reminder.ActorType, reminder.ActorId.ToString(), reminder.Name); + } - public override async Task UnregisterTimerAsync(ActorTimerToken timer) + public override async Task RegisterTimerAsync(ActorTimer timer) + { + if (timer == null) { - if (timer == null) - { - throw new ArgumentNullException(nameof(timer)); - } - - await this.interactor.UnregisterTimerAsync(timer.ActorType, timer.ActorId.ToString(), timer.Name); + throw new ArgumentNullException(nameof(timer)); } - private static async ValueTask SerializeReminderAsync(ActorReminder reminder) +#pragma warning disable 0618 + var timerInfo = new TimerInfo(timer.TimerCallback, timer.Data, timer.DueTime, timer.Period, timer.Ttl); +#pragma warning restore 0618 + var data = JsonSerializer.Serialize(timerInfo); + await this.interactor.RegisterTimerAsync(timer.ActorType, timer.ActorId.ToString(), timer.Name, data); + } + + public override async Task UnregisterTimerAsync(ActorTimerToken timer) + { + if (timer == null) { - var info = new ReminderInfo(reminder.State, reminder.DueTime, reminder.Period, reminder.Repetitions, - reminder.Ttl); - return await info.SerializeAsync(); + throw new ArgumentNullException(nameof(timer)); } - private static async ValueTask DeserializeReminderAsync(Stream stream, ActorReminderToken token) + await this.interactor.UnregisterTimerAsync(timer.ActorType, timer.ActorId.ToString(), timer.Name); + } + + private static async ValueTask SerializeReminderAsync(ActorReminder reminder) + { + var info = new ReminderInfo(reminder.State, reminder.DueTime, reminder.Period, reminder.Repetitions, + reminder.Ttl); + return await info.SerializeAsync(); + } + + private static async ValueTask DeserializeReminderAsync(Stream stream, ActorReminderToken token) + { + if (stream == null) { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - var info = await ReminderInfo.DeserializeAsync(stream); - if (info == null) - { - return null; - } + throw new ArgumentNullException(nameof(stream)); + } - var reminder = new ActorReminder(token.ActorType, token.ActorId, token.Name, info.Data, info.DueTime, - info.Period); - return reminder; + var info = await ReminderInfo.DeserializeAsync(stream); + if (info == null) + { + return null; } + + var reminder = new ActorReminder(token.ActorType, token.ActorId, token.Name, info.Data, info.DueTime, + info.Period); + return reminder; } -} +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/IActorContextualState.cs b/src/Dapr.Actors/Runtime/IActorContextualState.cs index 8d5ebaa2b..5854b9f95 100644 --- a/src/Dapr.Actors/Runtime/IActorContextualState.cs +++ b/src/Dapr.Actors/Runtime/IActorContextualState.cs @@ -13,15 +13,14 @@ using System.Threading.Tasks; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// +/// +internal interface IActorContextualState { /// - /// /// - internal interface IActorContextualState - { - /// - /// - Task SetStateContext(string stateContext); - } + Task SetStateContext(string stateContext); } \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/IActorReminder.cs b/src/Dapr.Actors/Runtime/IActorReminder.cs index 0f59efef3..b326fae17 100644 --- a/src/Dapr.Actors/Runtime/IActorReminder.cs +++ b/src/Dapr.Actors/Runtime/IActorReminder.cs @@ -11,43 +11,42 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime -{ - using System; +namespace Dapr.Actors.Runtime; + +using System; +/// +/// Represents a reminder registered using . +/// +public interface IActorReminder +{ /// - /// Represents a reminder registered using . + /// Gets the name of the reminder. The name is unique per actor. /// - public interface IActorReminder - { - /// - /// Gets the name of the reminder. The name is unique per actor. - /// - /// The name of the reminder. - string Name { get; } + /// The name of the reminder. + string Name { get; } - /// - /// Gets the time when the reminder is first due to be invoked. - /// - /// The time when the reminder is first due to be invoked. - /// - /// A value of negative one (-1) milliseconds means the reminder is not invoked. A value of zero (0) means the reminder is invoked immediately after registration. - /// - TimeSpan DueTime { get; } + /// + /// Gets the time when the reminder is first due to be invoked. + /// + /// The time when the reminder is first due to be invoked. + /// + /// A value of negative one (-1) milliseconds means the reminder is not invoked. A value of zero (0) means the reminder is invoked immediately after registration. + /// + TimeSpan DueTime { get; } - /// - /// Gets the time interval at which the reminder is invoked periodically. - /// - /// The time interval at which the reminder is invoked periodically. - /// - /// The first invocation of the reminder occurs after . All subsequent invocations occur at intervals defined by this property. - /// - TimeSpan Period { get; } + /// + /// Gets the time interval at which the reminder is invoked periodically. + /// + /// The time interval at which the reminder is invoked periodically. + /// + /// The first invocation of the reminder occurs after . All subsequent invocations occur at intervals defined by this property. + /// + TimeSpan Period { get; } - /// - /// Gets the user state passed to the reminder invocation. - /// - /// The user state passed to the reminder invocation. - byte[] State { get; } - } -} + /// + /// Gets the user state passed to the reminder invocation. + /// + /// The user state passed to the reminder invocation. + byte[] State { get; } +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/IActorStateManager.cs b/src/Dapr.Actors/Runtime/IActorStateManager.cs index 8795e28e5..88868d854 100644 --- a/src/Dapr.Actors/Runtime/IActorStateManager.cs +++ b/src/Dapr.Actors/Runtime/IActorStateManager.cs @@ -11,337 +11,336 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; +namespace Dapr.Actors.Runtime; + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +/// +/// Represents an interface that exposes methods to manage state of an . +/// This interface is implemented by . +/// +public interface IActorStateManager +{ /// - /// Represents an interface that exposes methods to manage state of an . - /// This interface is implemented by . + /// Adds an actor state with given state name. /// - public interface IActorStateManager - { - /// - /// Adds an actor state with given state name. - /// - /// Type of value associated with given state name. - /// Name of the actor state to add. - /// Value of the actor state to add. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous add operation. - /// - /// - /// An actor state with given state name already exists. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task AddStateAsync(string stateName, T value, CancellationToken cancellationToken = default); + /// Type of value associated with given state name. + /// Name of the actor state to add. + /// Value of the actor state to add. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous add operation. + /// + /// + /// An actor state with given state name already exists. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task AddStateAsync(string stateName, T value, CancellationToken cancellationToken = default); - /// - /// Adds an actor state with given state name. - /// - /// Type of value associated with given state name. - /// Name of the actor state to add. - /// Value of the actor state to add. - /// The token to monitor for cancellation requests. - /// The time to live for the state. - /// - /// A task that represents the asynchronous add operation. - /// - /// - /// An actor state with given state name already exists. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task AddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); + /// + /// Adds an actor state with given state name. + /// + /// Type of value associated with given state name. + /// Name of the actor state to add. + /// Value of the actor state to add. + /// The token to monitor for cancellation requests. + /// The time to live for the state. + /// + /// A task that represents the asynchronous add operation. + /// + /// + /// An actor state with given state name already exists. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task AddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); - /// - /// Gets an actor state with specified state name. - /// - /// Type of value associated with given state name. - /// Name of the actor state to get. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous get operation. The value of TResult - /// parameter contains value of actor state with given state name. - /// - /// - /// An actor state with given state name does not exist. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task GetStateAsync(string stateName, CancellationToken cancellationToken = default); + /// + /// Gets an actor state with specified state name. + /// + /// Type of value associated with given state name. + /// Name of the actor state to get. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous get operation. The value of TResult + /// parameter contains value of actor state with given state name. + /// + /// + /// An actor state with given state name does not exist. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task GetStateAsync(string stateName, CancellationToken cancellationToken = default); - /// - /// Sets an actor state with given state name to specified value. - /// If an actor state with specified name does not exist, it is added. - /// - /// Type of value associated with given state name. - /// Name of the actor state to set. - /// Value of the actor state. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous set operation. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task SetStateAsync(string stateName, T value, CancellationToken cancellationToken = default); + /// + /// Sets an actor state with given state name to specified value. + /// If an actor state with specified name does not exist, it is added. + /// + /// Type of value associated with given state name. + /// Name of the actor state to set. + /// Value of the actor state. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous set operation. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task SetStateAsync(string stateName, T value, CancellationToken cancellationToken = default); - /// - /// Sets an actor state with given state name to specified value. - /// If an actor state with specified name does not exist, it is added. - /// - /// Type of value associated with given state name. - /// Name of the actor state to set. - /// Value of the actor state. - /// The time to live for the state. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous set operation. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task SetStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); + /// + /// Sets an actor state with given state name to specified value. + /// If an actor state with specified name does not exist, it is added. + /// + /// Type of value associated with given state name. + /// Name of the actor state to set. + /// Value of the actor state. + /// The time to live for the state. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous set operation. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task SetStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); - /// - /// Removes an actor state with specified state name. - /// - /// Name of the actor state to remove. - /// The token to monitor for cancellation requests. - /// A task that represents the asynchronous remove operation. - /// - /// An actor state with given state name does not exist. - /// - /// The specified state name is null. - /// The operation was canceled. - Task RemoveStateAsync(string stateName, CancellationToken cancellationToken = default); + /// + /// Removes an actor state with specified state name. + /// + /// Name of the actor state to remove. + /// The token to monitor for cancellation requests. + /// A task that represents the asynchronous remove operation. + /// + /// An actor state with given state name does not exist. + /// + /// The specified state name is null. + /// The operation was canceled. + Task RemoveStateAsync(string stateName, CancellationToken cancellationToken = default); - /// - /// Attempts to add an actor state with given state name and value. Returns false if an actor state with - /// the same name already exists. - /// - /// Type of value associated with given state name. - /// Name of the actor state to add. - /// Value of the actor state to add. - /// The token to monitor for cancellation requests. - /// This is optional and defaults to . - /// - /// A boolean task that represents the asynchronous add operation. Returns true if the - /// value was successfully added and false if an actor state with the same name already exists. - /// - /// The specified state name is null. - /// Provide a valid state name string. - /// The request was canceled using the specified - /// . - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task TryAddStateAsync(string stateName, T value, CancellationToken cancellationToken = default); + /// + /// Attempts to add an actor state with given state name and value. Returns false if an actor state with + /// the same name already exists. + /// + /// Type of value associated with given state name. + /// Name of the actor state to add. + /// Value of the actor state to add. + /// The token to monitor for cancellation requests. + /// This is optional and defaults to . + /// + /// A boolean task that represents the asynchronous add operation. Returns true if the + /// value was successfully added and false if an actor state with the same name already exists. + /// + /// The specified state name is null. + /// Provide a valid state name string. + /// The request was canceled using the specified + /// . + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task TryAddStateAsync(string stateName, T value, CancellationToken cancellationToken = default); - /// - /// Attempts to add an actor state with given state name and value. Returns false if an actor state with - /// the same name already exists. - /// - /// Type of value associated with given state name. - /// Name of the actor state to add. - /// Value of the actor state to add. - /// The time to live for the state. - /// The token to monitor for cancellation requests. - /// This is optional and defaults to . - /// - /// A boolean task that represents the asynchronous add operation. Returns true if the - /// value was successfully added and false if an actor state with the same name already exists. - /// - /// The specified state name is null. - /// Provide a valid state name string. - /// The request was canceled using the specified - /// . - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task TryAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); + /// + /// Attempts to add an actor state with given state name and value. Returns false if an actor state with + /// the same name already exists. + /// + /// Type of value associated with given state name. + /// Name of the actor state to add. + /// Value of the actor state to add. + /// The time to live for the state. + /// The token to monitor for cancellation requests. + /// This is optional and defaults to . + /// + /// A boolean task that represents the asynchronous add operation. Returns true if the + /// value was successfully added and false if an actor state with the same name already exists. + /// + /// The specified state name is null. + /// Provide a valid state name string. + /// The request was canceled using the specified + /// . + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task TryAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); - /// - /// Attempts to get an actor state with specified state name. - /// - /// Type of value associated with given state name. - /// Name of the actor state to get. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous get operation. The value of TResult - /// parameter contains - /// indicating whether the actor state is present and the value of actor state if it is present. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task> TryGetStateAsync(string stateName, CancellationToken cancellationToken = default); + /// + /// Attempts to get an actor state with specified state name. + /// + /// Type of value associated with given state name. + /// Name of the actor state to get. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous get operation. The value of TResult + /// parameter contains + /// indicating whether the actor state is present and the value of actor state if it is present. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task> TryGetStateAsync(string stateName, CancellationToken cancellationToken = default); - /// - /// Attempts to remove an actor state with specified state name. - /// - /// Name of the actor state to remove. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous remove operation. The value of TResult - /// parameter indicates if the state was successfully removed. - /// - /// The specified state name is null. - /// The operation was canceled. - Task TryRemoveStateAsync(string stateName, CancellationToken cancellationToken = default); + /// + /// Attempts to remove an actor state with specified state name. + /// + /// Name of the actor state to remove. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous remove operation. The value of TResult + /// parameter indicates if the state was successfully removed. + /// + /// The specified state name is null. + /// The operation was canceled. + Task TryRemoveStateAsync(string stateName, CancellationToken cancellationToken = default); - /// - /// Checks if an actor state with specified name exists. - /// - /// Name of the actor state. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous check operation. The value of TResult - /// parameter is true if state with specified name exists otherwise false. - /// - /// The specified state name is null. - /// The operation was canceled. - Task ContainsStateAsync(string stateName, CancellationToken cancellationToken = default); + /// + /// Checks if an actor state with specified name exists. + /// + /// Name of the actor state. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous check operation. The value of TResult + /// parameter is true if state with specified name exists otherwise false. + /// + /// The specified state name is null. + /// The operation was canceled. + Task ContainsStateAsync(string stateName, CancellationToken cancellationToken = default); - /// - /// Gets an actor state with the given state name if it exists. If it does not - /// exist, creates and new state with the specified name and value. - /// - /// Type of value associated with given state name. - /// Name of the actor state to get or add. - /// Value of the actor state to add if it does not exist. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous get or add operation. The value of TResult - /// parameter contains value of actor state with given state name. - /// - /// The specified state name is null. - /// Provide a valid state name string. - /// The request was canceled using the specified - /// . - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task GetOrAddStateAsync(string stateName, T value, CancellationToken cancellationToken = default); + /// + /// Gets an actor state with the given state name if it exists. If it does not + /// exist, creates and new state with the specified name and value. + /// + /// Type of value associated with given state name. + /// Name of the actor state to get or add. + /// Value of the actor state to add if it does not exist. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous get or add operation. The value of TResult + /// parameter contains value of actor state with given state name. + /// + /// The specified state name is null. + /// Provide a valid state name string. + /// The request was canceled using the specified + /// . + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task GetOrAddStateAsync(string stateName, T value, CancellationToken cancellationToken = default); - /// - /// Gets an actor state with the given state name if it exists. If it does not - /// exist, creates and new state with the specified name and value. - /// - /// Type of value associated with given state name. - /// Name of the actor state to get or add. - /// Value of the actor state to add if it does not exist. - /// The time to live for the state. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous get or add operation. The value of TResult - /// parameter contains value of actor state with given state name. - /// - /// The specified state name is null. - /// Provide a valid state name string. - /// The request was canceled using the specified - /// . - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task GetOrAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); + /// + /// Gets an actor state with the given state name if it exists. If it does not + /// exist, creates and new state with the specified name and value. + /// + /// Type of value associated with given state name. + /// Name of the actor state to get or add. + /// Value of the actor state to add if it does not exist. + /// The time to live for the state. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous get or add operation. The value of TResult + /// parameter contains value of actor state with given state name. + /// + /// The specified state name is null. + /// Provide a valid state name string. + /// The request was canceled using the specified + /// . + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task GetOrAddStateAsync(string stateName, T value, TimeSpan ttl, CancellationToken cancellationToken = default); - /// - /// Adds an actor state with given state name, if it does not already exist or updates - /// the state with specified state name, if it exists. - /// - /// Type of value associated with given state name. - /// Name of the actor state to add or update. - /// Value of the actor state to add if it does not exist. - /// Factory function to generate value of actor state to update if it exists. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous add/update operation. The value of TResult - /// parameter contains value of actor state that was added/updated. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task AddOrUpdateStateAsync(string stateName, T addValue, Func updateValueFactory, CancellationToken cancellationToken = default); + /// + /// Adds an actor state with given state name, if it does not already exist or updates + /// the state with specified state name, if it exists. + /// + /// Type of value associated with given state name. + /// Name of the actor state to add or update. + /// Value of the actor state to add if it does not exist. + /// Factory function to generate value of actor state to update if it exists. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous add/update operation. The value of TResult + /// parameter contains value of actor state that was added/updated. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task AddOrUpdateStateAsync(string stateName, T addValue, Func updateValueFactory, CancellationToken cancellationToken = default); - /// - /// Adds an actor state with given state name, if it does not already exist or updates - /// the state with specified state name, if it exists. - /// - /// Type of value associated with given state name. - /// Name of the actor state to add or update. - /// Value of the actor state to add if it does not exist. - /// Factory function to generate value of actor state to update if it exists. - /// The time to live for the state. - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous add/update operation. The value of TResult - /// parameter contains value of actor state that was added/updated. - /// - /// The specified state name is null. - /// The operation was canceled. - /// - /// The type of state value must be - /// Data Contract serializable. - /// - Task AddOrUpdateStateAsync(string stateName, T addValue, Func updateValueFactory, TimeSpan ttl, CancellationToken cancellationToken = default); + /// + /// Adds an actor state with given state name, if it does not already exist or updates + /// the state with specified state name, if it exists. + /// + /// Type of value associated with given state name. + /// Name of the actor state to add or update. + /// Value of the actor state to add if it does not exist. + /// Factory function to generate value of actor state to update if it exists. + /// The time to live for the state. + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous add/update operation. The value of TResult + /// parameter contains value of actor state that was added/updated. + /// + /// The specified state name is null. + /// The operation was canceled. + /// + /// The type of state value must be + /// Data Contract serializable. + /// + Task AddOrUpdateStateAsync(string stateName, T addValue, Func updateValueFactory, TimeSpan ttl, CancellationToken cancellationToken = default); - /// - /// Clears all the cached actor states and any operation(s) performed on - /// since last state save operation. - /// - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous clear cache operation. - /// - /// - /// All the operation(s) performed on since last save operation are cleared on - /// clearing the cache and will not be included in next save operation. - /// - Task ClearCacheAsync(CancellationToken cancellationToken = default); + /// + /// Clears all the cached actor states and any operation(s) performed on + /// since last state save operation. + /// + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous clear cache operation. + /// + /// + /// All the operation(s) performed on since last save operation are cleared on + /// clearing the cache and will not be included in next save operation. + /// + Task ClearCacheAsync(CancellationToken cancellationToken = default); - /// - /// Saves all the cached state changes (add/update/remove) that were made since last call to - /// by actor runtime or by user explicitly. - /// - /// The token to monitor for cancellation requests. - /// - /// A task that represents the asynchronous save operation. - /// - Task SaveStateAsync(CancellationToken cancellationToken = default); - } -} + /// + /// Saves all the cached state changes (add/update/remove) that were made since last call to + /// by actor runtime or by user explicitly. + /// + /// The token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous save operation. + /// + Task SaveStateAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/IActorStateSerializer.cs b/src/Dapr.Actors/Runtime/IActorStateSerializer.cs index cff3b7c26..3d5390c77 100644 --- a/src/Dapr.Actors/Runtime/IActorStateSerializer.cs +++ b/src/Dapr.Actors/Runtime/IActorStateSerializer.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime -{ - using System; +namespace Dapr.Actors.Runtime; + +using System; +/// +/// Represents a custom serializer for actor state. +/// +internal interface IActorStateSerializer +{ /// - /// Represents a custom serializer for actor state. + /// Deserializes from the given byte array to . /// - internal interface IActorStateSerializer - { - /// - /// Deserializes from the given byte array to . - /// - /// The byte array to deserialize from. - /// The deserialized value. - T Deserialize(byte[] buffer); + /// The byte array to deserialize from. + /// The deserialized value. + T Deserialize(byte[] buffer); - /// - /// Serializes an object into a byte array. - /// - /// Type of the state. - /// The state value to serialize. - byte[] Serialize(Type stateType, T state); - } -} + /// + /// Serializes an object into a byte array. + /// + /// Type of the state. + /// The state value to serialize. + byte[] Serialize(Type stateType, T state); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/IRemindable.cs b/src/Dapr.Actors/Runtime/IRemindable.cs index 0fc66f903..21fb22fe9 100644 --- a/src/Dapr.Actors/Runtime/IRemindable.cs +++ b/src/Dapr.Actors/Runtime/IRemindable.cs @@ -11,28 +11,27 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime -{ - using System; - using System.Threading.Tasks; +namespace Dapr.Actors.Runtime; + +using System; +using System.Threading.Tasks; +/// +/// Interface that actors must implement to consume reminders registered using RegisterReminderAsync. />. +/// +public interface IRemindable +{ /// - /// Interface that actors must implement to consume reminders registered using RegisterReminderAsync. />. + /// The reminder call back invoked when an actor reminder is triggered. /// - public interface IRemindable - { - /// - /// The reminder call back invoked when an actor reminder is triggered. - /// - /// The name of reminder provided during registration. - /// The user state provided during registration. - /// The invocation due time provided during registration. - /// The invocation period provided during registration. - /// A task that represents the asynchronous operation performed by this callback. - /// - /// The state of this actor is saved by the actor runtime upon completion of the task returned by this method. If an error occurs while saving the state, then all state cached by this actor's will be discarded and reloaded from previously saved state when the next actor method or reminder invocation occurs. - /// - /// - Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period); - } -} + /// The name of reminder provided during registration. + /// The user state provided during registration. + /// The invocation due time provided during registration. + /// The invocation period provided during registration. + /// A task that represents the asynchronous operation performed by this callback. + /// + /// The state of this actor is saved by the actor runtime upon completion of the task returned by this method. If an error occurs while saving the state, then all state cached by this actor's will be discarded and reloaded from previously saved state when the next actor method or reminder invocation occurs. + /// + /// + Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period); +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/StateChangeKind.cs b/src/Dapr.Actors/Runtime/StateChangeKind.cs index 29269f2a8..3ad79893b 100644 --- a/src/Dapr.Actors/Runtime/StateChangeKind.cs +++ b/src/Dapr.Actors/Runtime/StateChangeKind.cs @@ -11,31 +11,30 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +/// +/// Represents the kind of state change for an actor state when saves change is called to a set of actor states. +/// +public enum StateChangeKind { /// - /// Represents the kind of state change for an actor state when saves change is called to a set of actor states. + /// No change in state. /// - public enum StateChangeKind - { - /// - /// No change in state. - /// - None = 0, + None = 0, - /// - /// The state needs to be added. - /// - Add = 1, + /// + /// The state needs to be added. + /// + Add = 1, - /// - /// The state needs to be updated. - /// - Update = 2, + /// + /// The state needs to be updated. + /// + Update = 2, - /// - /// The state needs to be removed. - /// - Remove = 3, - } -} + /// + /// The state needs to be removed. + /// + Remove = 3, +} \ No newline at end of file diff --git a/src/Dapr.Actors/Runtime/TimerInfo.cs b/src/Dapr.Actors/Runtime/TimerInfo.cs index bc206915a..71da4bf2c 100644 --- a/src/Dapr.Actors/Runtime/TimerInfo.cs +++ b/src/Dapr.Actors/Runtime/TimerInfo.cs @@ -11,165 +11,164 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +using System; +using System.Globalization; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using Dapr.Actors.Resources; + +/// +/// Represents the details of the timer set on an Actor. +/// +[Obsolete("This class is an implementation detail of the framework and will be made internal in a future release.")] +[JsonConverter(typeof(TimerInfoConverter))] +public class TimerInfo { - using System; - using System.Globalization; - using System.IO; - using System.Text.Json; - using System.Text.Json.Serialization; - using System.Threading; - using Dapr.Actors.Resources; - - /// - /// Represents the details of the timer set on an Actor. - /// - [Obsolete("This class is an implementation detail of the framework and will be made internal in a future release.")] - [JsonConverter(typeof(TimerInfoConverter))] - public class TimerInfo + private readonly TimeSpan minTimePeriod = Timeout.InfiniteTimeSpan; + + internal TimerInfo( + string callback, + byte[] state, + TimeSpan dueTime, + TimeSpan period, + TimeSpan? ttl = null) { - private readonly TimeSpan minTimePeriod = Timeout.InfiniteTimeSpan; - - internal TimerInfo( - string callback, - byte[] state, - TimeSpan dueTime, - TimeSpan period, - TimeSpan? ttl = null) - { - this.ValidateDueTime("DueTime", dueTime); - this.ValidatePeriod("Period", period); - this.Callback = callback; - this.Data = state; - this.DueTime = dueTime; - this.Period = period; - this.Ttl = ttl; - } + this.ValidateDueTime("DueTime", dueTime); + this.ValidatePeriod("Period", period); + this.Callback = callback; + this.Data = state; + this.DueTime = dueTime; + this.Period = period; + this.Ttl = ttl; + } - internal string Callback { get; private set; } + internal string Callback { get; private set; } - internal TimeSpan DueTime { get; private set; } + internal TimeSpan DueTime { get; private set; } - internal TimeSpan Period { get; private set; } + internal TimeSpan Period { get; private set; } - internal byte[] Data { get; private set; } + internal byte[] Data { get; private set; } - internal TimeSpan? Ttl { get; private set; } + internal TimeSpan? Ttl { get; private set; } - private void ValidateDueTime(string argName, TimeSpan value) + private void ValidateDueTime(string argName, TimeSpan value) + { + if (value < TimeSpan.Zero) { - if (value < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException( - argName, - string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - this.minTimePeriod.TotalMilliseconds, - TimeSpan.MaxValue.TotalMilliseconds)); - } + throw new ArgumentOutOfRangeException( + argName, + string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + this.minTimePeriod.TotalMilliseconds, + TimeSpan.MaxValue.TotalMilliseconds)); } + } - private void ValidatePeriod(string argName, TimeSpan value) + private void ValidatePeriod(string argName, TimeSpan value) + { + if (value < this.minTimePeriod) { - if (value < this.minTimePeriod) - { - throw new ArgumentOutOfRangeException( - argName, - string.Format( - CultureInfo.CurrentCulture, - SR.TimerArgumentOutOfRange, - this.minTimePeriod.TotalMilliseconds, - TimeSpan.MaxValue.TotalMilliseconds)); - } + throw new ArgumentOutOfRangeException( + argName, + string.Format( + CultureInfo.CurrentCulture, + SR.TimerArgumentOutOfRange, + this.minTimePeriod.TotalMilliseconds, + TimeSpan.MaxValue.TotalMilliseconds)); } } - #pragma warning disable 0618 - internal class TimerInfoConverter : JsonConverter +} +#pragma warning disable 0618 +internal class TimerInfoConverter : JsonConverter +{ + public override TimerInfo Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) { - public override TimerInfo Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - var dueTime = default(TimeSpan); - var period = default(TimeSpan); - var data = default(byte[]); - string callback = null; - TimeSpan? ttl = null; + var dueTime = default(TimeSpan); + var period = default(TimeSpan); + var data = default(byte[]); + string callback = null; + TimeSpan? ttl = null; - using (JsonDocument document = JsonDocument.ParseValue(ref reader)) - { - var json = document.RootElement.Clone(); - - if (json.TryGetProperty("dueTime", out var dueTimeProperty)) - { - var dueTimeString = dueTimeProperty.GetString(); - dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString); - } - - if (json.TryGetProperty("period", out var periodProperty)) - { - var periodString = periodProperty.GetString(); - period = ConverterUtils.ConvertTimeSpanFromDaprFormat(periodString); - } - - if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null) - { - data = dataProperty.GetBytesFromBase64(); - } - - if (json.TryGetProperty("callback", out var callbackProperty)) - { - callback = callbackProperty.GetString(); - } - - if (json.TryGetProperty("ttl", out var ttlProperty)) - { - var ttlString = ttlProperty.GetString(); - ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString); - } - - return new TimerInfo(callback, data, dueTime, period, ttl); - } - } - - public override async void Write( - Utf8JsonWriter writer, - TimerInfo value, - JsonSerializerOptions options) + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) { - using var stream = new MemoryStream(); + var json = document.RootElement.Clone(); - writer.WriteStartObject(); - if (value.DueTime != null) + if (json.TryGetProperty("dueTime", out var dueTimeProperty)) { - writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(value.DueTime)); + var dueTimeString = dueTimeProperty.GetString(); + dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString); } - if (value.Period != null && value.Period >= TimeSpan.Zero) + if (json.TryGetProperty("period", out var periodProperty)) { - writer.WriteString("period", ConverterUtils.ConvertTimeSpanValueInDaprFormat(value.Period)); + var periodString = periodProperty.GetString(); + period = ConverterUtils.ConvertTimeSpanFromDaprFormat(periodString); } - if (value.Data != null) + if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null) { - writer.WriteString("data", Convert.ToBase64String(value.Data)); + data = dataProperty.GetBytesFromBase64(); } - if (value.Callback != null) + if (json.TryGetProperty("callback", out var callbackProperty)) { - writer.WriteString("callback", value.Callback); + callback = callbackProperty.GetString(); } - if (value.Ttl != null) + if (json.TryGetProperty("ttl", out var ttlProperty)) { - writer.WriteString("ttl", ConverterUtils.ConvertTimeSpanValueInDaprFormat(value.Ttl)); + var ttlString = ttlProperty.GetString(); + ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString); } - writer.WriteEndObject(); - await writer.FlushAsync(); + return new TimerInfo(callback, data, dueTime, period, ttl); + } + } + + public override async void Write( + Utf8JsonWriter writer, + TimerInfo value, + JsonSerializerOptions options) + { + using var stream = new MemoryStream(); + + writer.WriteStartObject(); + if (value.DueTime != null) + { + writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(value.DueTime)); + } + + if (value.Period != null && value.Period >= TimeSpan.Zero) + { + writer.WriteString("period", ConverterUtils.ConvertTimeSpanValueInDaprFormat(value.Period)); + } + + if (value.Data != null) + { + writer.WriteString("data", Convert.ToBase64String(value.Data)); + } + + if (value.Callback != null) + { + writer.WriteString("callback", value.Callback); } + + if (value.Ttl != null) + { + writer.WriteString("ttl", ConverterUtils.ConvertTimeSpanValueInDaprFormat(value.Ttl)); + } + + writer.WriteEndObject(); + await writer.FlushAsync(); } - #pragma warning restore 0618 } +#pragma warning restore 0618 \ No newline at end of file diff --git a/src/Dapr.Actors/Serialization/ActorIdJsonConverter.cs b/src/Dapr.Actors/Serialization/ActorIdJsonConverter.cs index 5c3838f3b..30e608823 100644 --- a/src/Dapr.Actors/Serialization/ActorIdJsonConverter.cs +++ b/src/Dapr.Actors/Serialization/ActorIdJsonConverter.cs @@ -11,49 +11,48 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Seralization -{ - using System; - using System.Text.Json; - using System.Text.Json.Serialization; +namespace Dapr.Actors.Seralization; + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; - // Converter for ActorId - will be serialized as a JSON string - internal class ActorIdJsonConverter : JsonConverter +// Converter for ActorId - will be serialized as a JSON string +internal class ActorIdJsonConverter : JsonConverter +{ + public override ActorId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override ActorId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + if (typeToConvert != typeof(ActorId)) { - if (typeToConvert != typeof(ActorId)) - { - throw new ArgumentException( $"Conversion to the type '{typeToConvert}' is not supported.", nameof(typeToConvert)); - } - - // Note - we generate random Guids for Actor Ids when we're generating them randomly - // but we don't actually enforce a format. Ids could be a number, or a date, or whatever, - // we don't really care. However we always **represent** Ids in JSON as strings. - if (reader.TokenType == JsonTokenType.String && - reader.GetString() is string text && - !string.IsNullOrWhiteSpace(text)) - { - return new ActorId(text); - } - else if (reader.TokenType == JsonTokenType.Null) - { - return null; - } + throw new ArgumentException( $"Conversion to the type '{typeToConvert}' is not supported.", nameof(typeToConvert)); + } - throw new JsonException(); // The serializer will provide a default error message. + // Note - we generate random Guids for Actor Ids when we're generating them randomly + // but we don't actually enforce a format. Ids could be a number, or a date, or whatever, + // we don't really care. However we always **represent** Ids in JSON as strings. + if (reader.TokenType == JsonTokenType.String && + reader.GetString() is string text && + !string.IsNullOrWhiteSpace(text)) + { + return new ActorId(text); } + else if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + throw new JsonException(); // The serializer will provide a default error message. + } - public override void Write(Utf8JsonWriter writer, ActorId value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ActorId value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else { - if (value is null) - { - writer.WriteNullValue(); - } - else - { - writer.WriteStringValue(value.GetId()); - } + writer.WriteStringValue(value.GetId()); } } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/BulkMessageModel.cs b/src/Dapr.AspNetCore/BulkMessageModel.cs index 503101ef3..fe7951718 100644 --- a/src/Dapr.AspNetCore/BulkMessageModel.cs +++ b/src/Dapr.AspNetCore/BulkMessageModel.cs @@ -11,67 +11,66 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// Class representing an entry in the DaprBulkMessage. +/// +/// The type of value contained in the data. +public class BulkMessageModel { /// - /// Class representing an entry in the DaprBulkMessage. + /// Initializes a new instance of the class. /// - /// The type of value contained in the data. - public class BulkMessageModel - { - /// - /// Initializes a new instance of the class. - /// - public BulkMessageModel() { - } + public BulkMessageModel() { + } - /// - /// Initializes a new instance of the class. - /// - /// Identifier of the message being processed. - /// Source for this event. - /// Type of event. - /// Version of the event spec. - /// Type of the payload. - /// Payload. - public BulkMessageModel(string id, string source, string type, string specversion, string datacontenttype, - TValue data) { - this.Id = id; - this.Source = source; - this.Type = type; - this.Specversion = specversion; - this.Datacontenttype = datacontenttype; - this.Data = data; - } + /// + /// Initializes a new instance of the class. + /// + /// Identifier of the message being processed. + /// Source for this event. + /// Type of event. + /// Version of the event spec. + /// Type of the payload. + /// Payload. + public BulkMessageModel(string id, string source, string type, string specversion, string datacontenttype, + TValue data) { + this.Id = id; + this.Source = source; + this.Type = type; + this.Specversion = specversion; + this.Datacontenttype = datacontenttype; + this.Data = data; + } - /// - /// Identifier of the message being processed. - /// - public string Id { get; set; } + /// + /// Identifier of the message being processed. + /// + public string Id { get; set; } - /// - /// Source for this event. - /// - public string Source { get; set; } + /// + /// Source for this event. + /// + public string Source { get; set; } - /// - /// Type of event. - /// - public string Type { get; set; } + /// + /// Type of event. + /// + public string Type { get; set; } - /// - /// Version of the event spec. - /// - public string Specversion { get; set; } + /// + /// Version of the event spec. + /// + public string Specversion { get; set; } - /// - /// Type of the payload. - /// - public string Datacontenttype { get; set; } + /// + /// Type of the payload. + /// + public string Datacontenttype { get; set; } - /// - /// Payload. - /// - public TValue Data { get; set; } - } + /// + /// Payload. + /// + public TValue Data { get; set; } } diff --git a/src/Dapr.AspNetCore/BulkSubscribeAppResponse.cs b/src/Dapr.AspNetCore/BulkSubscribeAppResponse.cs index 9a9c32894..93c78dd5f 100644 --- a/src/Dapr.AspNetCore/BulkSubscribeAppResponse.cs +++ b/src/Dapr.AspNetCore/BulkSubscribeAppResponse.cs @@ -13,26 +13,25 @@ using System.Collections.Generic; -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// Response from the application containing status for each entry in the bulk message. +/// It is posted to the bulk subscribe handler. +/// +public class BulkSubscribeAppResponse { /// - /// Response from the application containing status for each entry in the bulk message. - /// It is posted to the bulk subscribe handler. + /// Initializes a new instance of the class. /// - public class BulkSubscribeAppResponse + /// List of statuses. + public BulkSubscribeAppResponse(List statuses) { - /// - /// Initializes a new instance of the class. - /// - /// List of statuses. - public BulkSubscribeAppResponse(List statuses) - { - this.Statuses = statuses; - } - - /// - /// List of statuses. - /// - public List Statuses { get; } + this.Statuses = statuses; } + + /// + /// List of statuses. + /// + public List Statuses { get; } } diff --git a/src/Dapr.AspNetCore/BulkSubscribeAppResponseEntry.cs b/src/Dapr.AspNetCore/BulkSubscribeAppResponseEntry.cs index b8f8f96df..f7e8de60b 100644 --- a/src/Dapr.AspNetCore/BulkSubscribeAppResponseEntry.cs +++ b/src/Dapr.AspNetCore/BulkSubscribeAppResponseEntry.cs @@ -11,32 +11,31 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// Maps an entry from bulk subscribe messages to a response status. +/// +public class BulkSubscribeAppResponseEntry { + /// - /// Maps an entry from bulk subscribe messages to a response status. + /// Initializes a new instance of the class. /// - public class BulkSubscribeAppResponseEntry - { - - /// - /// Initializes a new instance of the class. - /// - /// Entry ID of the event. - /// Status of the event processing in application. - public BulkSubscribeAppResponseEntry(string entryId, BulkSubscribeAppResponseStatus status) { - this.EntryId = entryId; - this.Status = status.ToString(); - } + /// Entry ID of the event. + /// Status of the event processing in application. + public BulkSubscribeAppResponseEntry(string entryId, BulkSubscribeAppResponseStatus status) { + this.EntryId = entryId; + this.Status = status.ToString(); + } - /// - /// Entry ID of the event. - /// - public string EntryId { get; } + /// + /// Entry ID of the event. + /// + public string EntryId { get; } - /// - /// Status of the event processing in application. - /// - public string Status { get; } - } + /// + /// Status of the event processing in application. + /// + public string Status { get; } } diff --git a/src/Dapr.AspNetCore/BulkSubscribeAppResponseStatus.cs b/src/Dapr.AspNetCore/BulkSubscribeAppResponseStatus.cs index 55c8ad468..a275edb3b 100644 --- a/src/Dapr.AspNetCore/BulkSubscribeAppResponseStatus.cs +++ b/src/Dapr.AspNetCore/BulkSubscribeAppResponseStatus.cs @@ -11,24 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore -{ - /// - /// Status of the message handled in bulk subscribe handler. - /// - public enum BulkSubscribeAppResponseStatus { - /// - /// Success - /// - SUCCESS, - /// - /// Failure - /// - RETRY, - /// - /// Drop - /// - DROP - } - -} +namespace Dapr.AspNetCore; + +/// +/// Status of the message handled in bulk subscribe handler. +/// +public enum BulkSubscribeAppResponseStatus { + /// + /// Success + /// + SUCCESS, + /// + /// Failure + /// + RETRY, + /// + /// Drop + /// + DROP +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/BulkSubscribeAttribute.cs b/src/Dapr.AspNetCore/BulkSubscribeAttribute.cs index f5331bdf4..db63a1896 100644 --- a/src/Dapr.AspNetCore/BulkSubscribeAttribute.cs +++ b/src/Dapr.AspNetCore/BulkSubscribeAttribute.cs @@ -13,62 +13,61 @@ using System; -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// BulkSubscribeAttribute describes options for a bulk subscriber with respect to a topic. +/// It needs to be paired with at least one [Topic] depending on the use case. +/// +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +public class BulkSubscribeAttribute : Attribute, IBulkSubscribeMetadata { /// - /// BulkSubscribeAttribute describes options for a bulk subscriber with respect to a topic. - /// It needs to be paired with at least one [Topic] depending on the use case. + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class BulkSubscribeAttribute : Attribute, IBulkSubscribeMetadata + /// The name of topic. + /// The name of the pubsub component to use. + /// The topic name. + public BulkSubscribeAttribute(string topicName, int maxMessagesCount, int maxAwaitDurationMs) { - /// - /// Initializes a new instance of the class. - /// - /// The name of topic. - /// The name of the pubsub component to use. - /// The topic name. - public BulkSubscribeAttribute(string topicName, int maxMessagesCount, int maxAwaitDurationMs) - { - this.TopicName = topicName; - this.MaxMessagesCount = maxMessagesCount; - this.MaxAwaitDurationMs = maxAwaitDurationMs; - } + this.TopicName = topicName; + this.MaxMessagesCount = maxMessagesCount; + this.MaxAwaitDurationMs = maxAwaitDurationMs; + } - /// - /// Initializes a new instance of the class. - /// - /// The name of topic. - /// The name of the pubsub component to use. - public BulkSubscribeAttribute(string topicName, int maxMessagesCount) - { - this.TopicName = topicName; - this.MaxMessagesCount = maxMessagesCount; - } + /// + /// Initializes a new instance of the class. + /// + /// The name of topic. + /// The name of the pubsub component to use. + public BulkSubscribeAttribute(string topicName, int maxMessagesCount) + { + this.TopicName = topicName; + this.MaxMessagesCount = maxMessagesCount; + } - /// - /// Initializes a new instance of the class. - /// - /// The name of topic. - public BulkSubscribeAttribute(string topicName) - { - this.TopicName = topicName; - } + /// + /// Initializes a new instance of the class. + /// + /// The name of topic. + public BulkSubscribeAttribute(string topicName) + { + this.TopicName = topicName; + } - /// - /// Maximum number of messages in a bulk message from the message bus. - /// - public int MaxMessagesCount { get; } = 100; + /// + /// Maximum number of messages in a bulk message from the message bus. + /// + public int MaxMessagesCount { get; } = 100; - /// - /// Maximum duration to wait for maxBulkSubCount messages by the message bus - /// before sending the messages to Dapr. - /// - public int MaxAwaitDurationMs { get; } = 1000; + /// + /// Maximum duration to wait for maxBulkSubCount messages by the message bus + /// before sending the messages to Dapr. + /// + public int MaxAwaitDurationMs { get; } = 1000; - /// - /// The name of the topic to be bulk subscribed. - /// - public string TopicName { get; } - } -} + /// + /// The name of the topic to be bulk subscribed. + /// + public string TopicName { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/BulkSubscribeMessage.cs b/src/Dapr.AspNetCore/BulkSubscribeMessage.cs index f00ba373a..cf39c0ef3 100644 --- a/src/Dapr.AspNetCore/BulkSubscribeMessage.cs +++ b/src/Dapr.AspNetCore/BulkSubscribeMessage.cs @@ -13,47 +13,46 @@ using System.Collections.Generic; -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// Represents a bulk of messages received from the message bus. +/// +/// The type of value contained in the data. +public class BulkSubscribeMessage { /// - /// Represents a bulk of messages received from the message bus. + /// Initializes a new instance of the class. /// - /// The type of value contained in the data. - public class BulkSubscribeMessage + public BulkSubscribeMessage() { - /// - /// Initializes a new instance of the class. - /// - public BulkSubscribeMessage() - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// A list of entries representing the event and other metadata. - /// The name of the pubsub topic. - /// Metadata for the bulk message. - public BulkSubscribeMessage(List> entries, string topic, Dictionary metadata) - { - this.Entries = entries; - this.Topic = topic; - this.Metadata = metadata; - } + /// + /// Initializes a new instance of the class. + /// + /// A list of entries representing the event and other metadata. + /// The name of the pubsub topic. + /// Metadata for the bulk message. + public BulkSubscribeMessage(List> entries, string topic, Dictionary metadata) + { + this.Entries = entries; + this.Topic = topic; + this.Metadata = metadata; + } - /// - /// A list of entries representing the event and other metadata. - /// - public List> Entries { get; set; } + /// + /// A list of entries representing the event and other metadata. + /// + public List> Entries { get; set; } - /// - /// The name of the pubsub topic. - /// - public string Topic { get; set; } + /// + /// The name of the pubsub topic. + /// + public string Topic { get; set; } - /// - /// Metadata for the bulk message. - /// - public Dictionary Metadata { get; set; } - } -} + /// + /// Metadata for the bulk message. + /// + public Dictionary Metadata { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/BulkSubscribeMessageEntry.cs b/src/Dapr.AspNetCore/BulkSubscribeMessageEntry.cs index 62abe3a12..aba31e714 100644 --- a/src/Dapr.AspNetCore/BulkSubscribeMessageEntry.cs +++ b/src/Dapr.AspNetCore/BulkSubscribeMessageEntry.cs @@ -13,55 +13,54 @@ using System.Collections.Generic; -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// Represents a single event from a bulk of messages sent by the message bus. +/// +/// The type of value contained in the data. +public class BulkSubscribeMessageEntry { /// - /// Represents a single event from a bulk of messages sent by the message bus. + /// Initializes a new instance of the class. /// - /// The type of value contained in the data. - public class BulkSubscribeMessageEntry - { - /// - /// Initializes a new instance of the class. - /// - public BulkSubscribeMessageEntry() { - } + public BulkSubscribeMessageEntry() { + } - /// - /// Initializes a new instance of the class. - /// - /// A unique identifier for the event. - /// Content type of the event. - /// Metadata for the event. - /// The pubsub event. - public BulkSubscribeMessageEntry(string entryId, string contentType, Dictionary metadata, - TValue eventData) - { - this.EntryId = entryId; - this.ContentType = contentType; - this.Metadata = metadata; - this.Event = eventData; - } + /// + /// Initializes a new instance of the class. + /// + /// A unique identifier for the event. + /// Content type of the event. + /// Metadata for the event. + /// The pubsub event. + public BulkSubscribeMessageEntry(string entryId, string contentType, Dictionary metadata, + TValue eventData) + { + this.EntryId = entryId; + this.ContentType = contentType; + this.Metadata = metadata; + this.Event = eventData; + } - /// - /// A unique identifier for the event. - /// - public string EntryId { get; set; } + /// + /// A unique identifier for the event. + /// + public string EntryId { get; set; } - /// - /// Content type of the event. - /// - public string ContentType { get; set; } + /// + /// Content type of the event. + /// + public string ContentType { get; set; } - /// - /// Metadata for the event. - /// - public Dictionary Metadata { get; set; } + /// + /// Metadata for the event. + /// + public Dictionary Metadata { get; set; } - /// - /// The pubsub event. - /// - public TValue Event { get; set; } + /// + /// The pubsub event. + /// + public TValue Event { get; set; } - } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/BulkSubscribeTopicOptions.cs b/src/Dapr.AspNetCore/BulkSubscribeTopicOptions.cs index d70e4cfd1..2dfec885c 100644 --- a/src/Dapr.AspNetCore/BulkSubscribeTopicOptions.cs +++ b/src/Dapr.AspNetCore/BulkSubscribeTopicOptions.cs @@ -1,24 +1,23 @@ -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// This class defines configurations for the bulk subscribe endpoint. +/// +public class BulkSubscribeTopicOptions { /// - /// This class defines configurations for the bulk subscribe endpoint. + /// Maximum number of messages in a bulk message from the message bus. /// - public class BulkSubscribeTopicOptions - { - /// - /// Maximum number of messages in a bulk message from the message bus. - /// - public int MaxMessagesCount { get; set; } = 100; + public int MaxMessagesCount { get; set; } = 100; - /// - /// Maximum duration to wait for maxBulkSubCount messages by the message bus - /// before sending the messages to Dapr. - /// - public int MaxAwaitDurationMs { get; set; } = 1000; + /// + /// Maximum duration to wait for maxBulkSubCount messages by the message bus + /// before sending the messages to Dapr. + /// + public int MaxAwaitDurationMs { get; set; } = 1000; - /// - /// The name of the topic to be bulk subscribed. - /// - public string TopicName { get; set; } - } -} + /// + /// The name of the topic to be bulk subscribed. + /// + public string TopicName { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/CloudEventPropertyNames.cs b/src/Dapr.AspNetCore/CloudEventPropertyNames.cs index 87e496004..a43d87da6 100644 --- a/src/Dapr.AspNetCore/CloudEventPropertyNames.cs +++ b/src/Dapr.AspNetCore/CloudEventPropertyNames.cs @@ -1,9 +1,8 @@ -namespace Dapr +namespace Dapr; + +internal static class CloudEventPropertyNames { - internal static class CloudEventPropertyNames - { - public const string Data = "data"; - public const string DataContentType = "datacontenttype"; - public const string DataBase64 = "data_base64"; - } -} + public const string Data = "data"; + public const string DataContentType = "datacontenttype"; + public const string DataBase64 = "data_base64"; +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/CloudEventsMiddleware.cs b/src/Dapr.AspNetCore/CloudEventsMiddleware.cs index eac526c26..b9b64b72c 100644 --- a/src/Dapr.AspNetCore/CloudEventsMiddleware.cs +++ b/src/Dapr.AspNetCore/CloudEventsMiddleware.cs @@ -14,283 +14,282 @@ using System.Collections.Generic; using System.Linq; -namespace Dapr +namespace Dapr; + +using System; +using System.IO; +using System.Net; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; + +internal class CloudEventsMiddleware { - using System; - using System.IO; - using System.Net; - using System.Text; - using System.Text.Json; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.WebUtilities; - using Microsoft.Extensions.Primitives; - using Microsoft.Net.Http.Headers; - - internal class CloudEventsMiddleware + private const string ContentType = "application/cloudevents+json"; + + // These cloudevent properties are either containing the body of the message or + // are included in the headers by other components of Dapr earlier in the pipeline + private static readonly string[] ExcludedPropertiesFromHeaders = { - private const string ContentType = "application/cloudevents+json"; + CloudEventPropertyNames.DataContentType, CloudEventPropertyNames.Data, + CloudEventPropertyNames.DataBase64, "pubsubname", "traceparent" + }; - // These cloudevent properties are either containing the body of the message or - // are included in the headers by other components of Dapr earlier in the pipeline - private static readonly string[] ExcludedPropertiesFromHeaders = - { - CloudEventPropertyNames.DataContentType, CloudEventPropertyNames.Data, - CloudEventPropertyNames.DataBase64, "pubsubname", "traceparent" - }; + private readonly RequestDelegate next; + private readonly CloudEventsMiddlewareOptions options; - private readonly RequestDelegate next; - private readonly CloudEventsMiddlewareOptions options; + public CloudEventsMiddleware(RequestDelegate next, CloudEventsMiddlewareOptions options) + { + this.next = next; + this.options = options; + } - public CloudEventsMiddleware(RequestDelegate next, CloudEventsMiddlewareOptions options) + public Task InvokeAsync(HttpContext httpContext) + { + // This middleware unwraps any requests with a cloud events (JSON) content type + // and replaces the request body + request content type so that it can be read by a + // non-cloud-events-aware piece of code. + // + // This corresponds to cloud events in the *structured* format: + // https://github.com/cloudevents/spec/blob/master/http-transport-binding.md#13-content-modes + // + // For *binary* format, we don't have to do anything + // + // We don't support batching. + // + // The philosophy here is that we don't report an error for things we don't support, because + // that would block someone from implementing their own support for it. We only report an error + // when something we do support isn't correct. + if (!MatchesContentType(httpContext, out var charSet)) { - this.next = next; - this.options = options; + return this.next(httpContext); } - public Task InvokeAsync(HttpContext httpContext) - { - // This middleware unwraps any requests with a cloud events (JSON) content type - // and replaces the request body + request content type so that it can be read by a - // non-cloud-events-aware piece of code. - // - // This corresponds to cloud events in the *structured* format: - // https://github.com/cloudevents/spec/blob/master/http-transport-binding.md#13-content-modes - // - // For *binary* format, we don't have to do anything - // - // We don't support batching. - // - // The philosophy here is that we don't report an error for things we don't support, because - // that would block someone from implementing their own support for it. We only report an error - // when something we do support isn't correct. - if (!MatchesContentType(httpContext, out var charSet)) - { - return this.next(httpContext); - } + return this.ProcessBodyAsync(httpContext, charSet); + } - return this.ProcessBodyAsync(httpContext, charSet); + private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) + { + JsonElement json; + if (string.Equals(charSet, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase)) + { + json = await JsonSerializer.DeserializeAsync(httpContext.Request.Body); } - - private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) + else { - JsonElement json; - if (string.Equals(charSet, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase)) + using (var reader = + new HttpRequestStreamReader(httpContext.Request.Body, Encoding.GetEncoding(charSet))) { - json = await JsonSerializer.DeserializeAsync(httpContext.Request.Body); - } - else - { - using (var reader = - new HttpRequestStreamReader(httpContext.Request.Body, Encoding.GetEncoding(charSet))) - { - var text = await reader.ReadToEndAsync(); - json = JsonSerializer.Deserialize(text); - } + var text = await reader.ReadToEndAsync(); + json = JsonSerializer.Deserialize(text); } + } - Stream originalBody; - Stream body; + Stream originalBody; + Stream body; - string originalContentType; - string contentType; + string originalContentType; + string contentType; - // Check whether to use data or data_base64 as per https://github.com/cloudevents/spec/blob/v1.0.1/json-format.md#31-handling-of-data - // Get the property names by OrdinalIgnoreCase comparison to support case insensitive JSON as the Json Serializer for AspCore already supports it by default. - var jsonPropNames = json.EnumerateObject().ToArray(); + // Check whether to use data or data_base64 as per https://github.com/cloudevents/spec/blob/v1.0.1/json-format.md#31-handling-of-data + // Get the property names by OrdinalIgnoreCase comparison to support case insensitive JSON as the Json Serializer for AspCore already supports it by default. + var jsonPropNames = json.EnumerateObject().ToArray(); - var dataPropName = jsonPropNames - .Select(d => d.Name) - .FirstOrDefault(d => d.Equals(CloudEventPropertyNames.Data, StringComparison.OrdinalIgnoreCase)); + var dataPropName = jsonPropNames + .Select(d => d.Name) + .FirstOrDefault(d => d.Equals(CloudEventPropertyNames.Data, StringComparison.OrdinalIgnoreCase)); - var dataBase64PropName = jsonPropNames - .Select(d => d.Name) - .FirstOrDefault(d => - d.Equals(CloudEventPropertyNames.DataBase64, StringComparison.OrdinalIgnoreCase)); + var dataBase64PropName = jsonPropNames + .Select(d => d.Name) + .FirstOrDefault(d => + d.Equals(CloudEventPropertyNames.DataBase64, StringComparison.OrdinalIgnoreCase)); - var isDataSet = false; - var isBinaryDataSet = false; - JsonElement data = default; + var isDataSet = false; + var isBinaryDataSet = false; + JsonElement data = default; - if (dataPropName != null) - { - isDataSet = true; - data = json.TryGetProperty(dataPropName, out var dataJsonElement) ? dataJsonElement : data; - } + if (dataPropName != null) + { + isDataSet = true; + data = json.TryGetProperty(dataPropName, out var dataJsonElement) ? dataJsonElement : data; + } - if (dataBase64PropName != null) - { - isBinaryDataSet = true; - data = json.TryGetProperty(dataBase64PropName, out var dataJsonElement) ? dataJsonElement : data; - } + if (dataBase64PropName != null) + { + isBinaryDataSet = true; + data = json.TryGetProperty(dataBase64PropName, out var dataJsonElement) ? dataJsonElement : data; + } - if (isDataSet && isBinaryDataSet) - { - httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; - return; - } + if (isDataSet && isBinaryDataSet) + { + httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + return; + } - if (isDataSet) - { - contentType = GetDataContentType(json, out var isJson); - - // If the value is anything other than a JSON string, treat it as JSON. Cloud Events requires - // non-JSON text to be enclosed in a JSON string. - isJson |= data.ValueKind != JsonValueKind.String; - - body = new MemoryStream(); - if (isJson || options.SuppressJsonDecodingOfTextPayloads) - { - // Rehydrate body from JSON payload - await JsonSerializer.SerializeAsync(body, data); - } - else - { - // Rehydrate body from contents of the string - var text = data.GetString(); - await using var writer = new HttpResponseStreamWriter(body, Encoding.UTF8); - await writer.WriteAsync(text); - } - - body.Seek(0L, SeekOrigin.Begin); - } - else if (isBinaryDataSet) + if (isDataSet) + { + contentType = GetDataContentType(json, out var isJson); + + // If the value is anything other than a JSON string, treat it as JSON. Cloud Events requires + // non-JSON text to be enclosed in a JSON string. + isJson |= data.ValueKind != JsonValueKind.String; + + body = new MemoryStream(); + if (isJson || options.SuppressJsonDecodingOfTextPayloads) { - // As per the spec, if the implementation determines that the type of data is Binary, - // the value MUST be represented as a JSON string expression containing the Base64 encoded - // binary value, and use the member name data_base64 to store it inside the JSON object. - var decodedBody = data.GetBytesFromBase64(); - body = new MemoryStream(decodedBody); - body.Seek(0L, SeekOrigin.Begin); - contentType = GetDataContentType(json, out _); + // Rehydrate body from JSON payload + await JsonSerializer.SerializeAsync(body, data); } else { - body = new MemoryStream(); - contentType = null; + // Rehydrate body from contents of the string + var text = data.GetString(); + await using var writer = new HttpResponseStreamWriter(body, Encoding.UTF8); + await writer.WriteAsync(text); } - ForwardCloudEventPropertiesAsHeaders(httpContext, jsonPropNames); + body.Seek(0L, SeekOrigin.Begin); + } + else if (isBinaryDataSet) + { + // As per the spec, if the implementation determines that the type of data is Binary, + // the value MUST be represented as a JSON string expression containing the Base64 encoded + // binary value, and use the member name data_base64 to store it inside the JSON object. + var decodedBody = data.GetBytesFromBase64(); + body = new MemoryStream(decodedBody); + body.Seek(0L, SeekOrigin.Begin); + contentType = GetDataContentType(json, out _); + } + else + { + body = new MemoryStream(); + contentType = null; + } + + ForwardCloudEventPropertiesAsHeaders(httpContext, jsonPropNames); - originalBody = httpContext.Request.Body; - originalContentType = httpContext.Request.ContentType; + originalBody = httpContext.Request.Body; + originalContentType = httpContext.Request.ContentType; - try - { - httpContext.Request.Body = body; - httpContext.Request.ContentType = contentType; + try + { + httpContext.Request.Body = body; + httpContext.Request.ContentType = contentType; - await this.next(httpContext); - } - finally - { - httpContext.Request.ContentType = originalContentType; - httpContext.Request.Body = originalBody; - } + await this.next(httpContext); } - - private void ForwardCloudEventPropertiesAsHeaders( - HttpContext httpContext, - IEnumerable jsonPropNames) + finally { - if (!options.ForwardCloudEventPropertiesAsHeaders) - { - return; - } + httpContext.Request.ContentType = originalContentType; + httpContext.Request.Body = originalBody; + } + } - var filteredPropertyNames = jsonPropNames - .Where(d => !ExcludedPropertiesFromHeaders.Contains(d.Name, StringComparer.OrdinalIgnoreCase)); + private void ForwardCloudEventPropertiesAsHeaders( + HttpContext httpContext, + IEnumerable jsonPropNames) + { + if (!options.ForwardCloudEventPropertiesAsHeaders) + { + return; + } - if (options.IncludedCloudEventPropertiesAsHeaders != null) - { - filteredPropertyNames = filteredPropertyNames - .Where(d => options.IncludedCloudEventPropertiesAsHeaders - .Contains(d.Name, StringComparer.OrdinalIgnoreCase)); - } - else if (options.ExcludedCloudEventPropertiesFromHeaders != null) - { - filteredPropertyNames = filteredPropertyNames - .Where(d => !options.ExcludedCloudEventPropertiesFromHeaders - .Contains(d.Name, StringComparer.OrdinalIgnoreCase)); - } + var filteredPropertyNames = jsonPropNames + .Where(d => !ExcludedPropertiesFromHeaders.Contains(d.Name, StringComparer.OrdinalIgnoreCase)); - foreach (var jsonProperty in filteredPropertyNames) - { - httpContext.Request.Headers.TryAdd($"Cloudevent.{jsonProperty.Name.ToLowerInvariant()}", - jsonProperty.Value.GetRawText().Trim('\"')); - } + if (options.IncludedCloudEventPropertiesAsHeaders != null) + { + filteredPropertyNames = filteredPropertyNames + .Where(d => options.IncludedCloudEventPropertiesAsHeaders + .Contains(d.Name, StringComparer.OrdinalIgnoreCase)); } - - private static string GetDataContentType(JsonElement json, out bool isJson) + else if (options.ExcludedCloudEventPropertiesFromHeaders != null) { - var dataContentTypePropName = json - .EnumerateObject() - .Select(d => d.Name) - .FirstOrDefault(d => - d.Equals(CloudEventPropertyNames.DataContentType, - StringComparison.OrdinalIgnoreCase)); - - string contentType; - - if (dataContentTypePropName != null - && json.TryGetProperty(dataContentTypePropName, out var dataContentType) - && dataContentType.ValueKind == JsonValueKind.String - && MediaTypeHeaderValue.TryParse(dataContentType.GetString(), out var parsed)) - { - contentType = dataContentType.GetString(); - isJson = - parsed.MediaType.Equals("application/json", StringComparison.Ordinal) || - parsed.Suffix.EndsWith("+json", StringComparison.Ordinal); - - // Since S.T.Json always outputs utf-8, we may need to normalize the data content type - // to remove any charset information. We generally just assume utf-8 everywhere, so omitting - // a charset is a safe bet. - if (contentType.Contains("charset")) - { - parsed.Charset = StringSegment.Empty; - contentType = parsed.ToString(); - } - } - else - { - // assume JSON is not specified. - contentType = "application/json"; - isJson = true; - } + filteredPropertyNames = filteredPropertyNames + .Where(d => !options.ExcludedCloudEventPropertiesFromHeaders + .Contains(d.Name, StringComparer.OrdinalIgnoreCase)); + } - return contentType; + foreach (var jsonProperty in filteredPropertyNames) + { + httpContext.Request.Headers.TryAdd($"Cloudevent.{jsonProperty.Name.ToLowerInvariant()}", + jsonProperty.Value.GetRawText().Trim('\"')); } + } - private static bool MatchesContentType(HttpContext httpContext, out string charSet) + private static string GetDataContentType(JsonElement json, out bool isJson) + { + var dataContentTypePropName = json + .EnumerateObject() + .Select(d => d.Name) + .FirstOrDefault(d => + d.Equals(CloudEventPropertyNames.DataContentType, + StringComparison.OrdinalIgnoreCase)); + + string contentType; + + if (dataContentTypePropName != null + && json.TryGetProperty(dataContentTypePropName, out var dataContentType) + && dataContentType.ValueKind == JsonValueKind.String + && MediaTypeHeaderValue.TryParse(dataContentType.GetString(), out var parsed)) { - if (httpContext.Request.ContentType == null) + contentType = dataContentType.GetString(); + isJson = + parsed.MediaType.Equals("application/json", StringComparison.Ordinal) || + parsed.Suffix.EndsWith("+json", StringComparison.Ordinal); + + // Since S.T.Json always outputs utf-8, we may need to normalize the data content type + // to remove any charset information. We generally just assume utf-8 everywhere, so omitting + // a charset is a safe bet. + if (contentType.Contains("charset")) { - charSet = null; - return false; + parsed.Charset = StringSegment.Empty; + contentType = parsed.ToString(); } + } + else + { + // assume JSON is not specified. + contentType = "application/json"; + isJson = true; + } - // Handle cases where the content type includes additional parameters like charset. - // Doing the string comparison up front so we can avoid allocation. - if (!httpContext.Request.ContentType.StartsWith(ContentType)) - { - charSet = null; - return false; - } + return contentType; + } - if (!MediaTypeHeaderValue.TryParse(httpContext.Request.ContentType, out var parsed)) - { - charSet = null; - return false; - } + private static bool MatchesContentType(HttpContext httpContext, out string charSet) + { + if (httpContext.Request.ContentType == null) + { + charSet = null; + return false; + } - if (parsed.MediaType != ContentType) - { - charSet = null; - return false; - } + // Handle cases where the content type includes additional parameters like charset. + // Doing the string comparison up front so we can avoid allocation. + if (!httpContext.Request.ContentType.StartsWith(ContentType)) + { + charSet = null; + return false; + } - charSet = parsed.Charset.Length > 0 ? parsed.Charset.Value : "UTF-8"; - return true; + if (!MediaTypeHeaderValue.TryParse(httpContext.Request.ContentType, out var parsed)) + { + charSet = null; + return false; } + + if (parsed.MediaType != ContentType) + { + charSet = null; + return false; + } + + charSet = parsed.Charset.Length > 0 ? parsed.Charset.Value : "UTF-8"; + return true; } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs b/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs index 84e68adb5..98311013b 100644 --- a/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs +++ b/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs @@ -11,67 +11,66 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// Provides optional settings to the cloud events middleware. +/// +public class CloudEventsMiddlewareOptions { /// - /// Provides optional settings to the cloud events middleware. + /// Gets or sets a value that will determine whether non-JSON textual payloads are decoded. /// - public class CloudEventsMiddlewareOptions - { - /// - /// Gets or sets a value that will determine whether non-JSON textual payloads are decoded. - /// - /// - /// - /// In the 1.0 release of the Dapr .NET SDK the cloud events middleware would not JSON-decode - /// a textual cloud events payload. A cloud event payload containing text/plain data - /// of "data": "Hello, \"world!\"" would result in a request body containing "Hello, \"world!\"" - /// instead of the expected JSON-decoded value of Hello, "world!". - /// - /// - /// Setting this property to true restores the previous invalid behavior for compatibility. - /// - /// - public bool SuppressJsonDecodingOfTextPayloads { get; set; } + /// + /// + /// In the 1.0 release of the Dapr .NET SDK the cloud events middleware would not JSON-decode + /// a textual cloud events payload. A cloud event payload containing text/plain data + /// of "data": "Hello, \"world!\"" would result in a request body containing "Hello, \"world!\"" + /// instead of the expected JSON-decoded value of Hello, "world!". + /// + /// + /// Setting this property to true restores the previous invalid behavior for compatibility. + /// + /// + public bool SuppressJsonDecodingOfTextPayloads { get; set; } - /// - /// Gets or sets a value that will determine whether the CloudEvent properties will be forwarded as Request Headers. - /// - /// - /// - /// Setting this property to true will forward all the CloudEvent properties as Request Headers. - /// For more fine grained control of which properties are forwarded you can use either or . - /// - /// - /// Property names will always be prefixed with 'Cloudevent.' and be lower case in the following format:"Cloudevent.type" - /// - /// - /// ie. A CloudEvent property "type": "Example.Type" will be added as "Cloudevent.type": "Example.Type" request header. - /// - /// - public bool ForwardCloudEventPropertiesAsHeaders { get; set; } + /// + /// Gets or sets a value that will determine whether the CloudEvent properties will be forwarded as Request Headers. + /// + /// + /// + /// Setting this property to true will forward all the CloudEvent properties as Request Headers. + /// For more fine grained control of which properties are forwarded you can use either or . + /// + /// + /// Property names will always be prefixed with 'Cloudevent.' and be lower case in the following format:"Cloudevent.type" + /// + /// + /// ie. A CloudEvent property "type": "Example.Type" will be added as "Cloudevent.type": "Example.Type" request header. + /// + /// + public bool ForwardCloudEventPropertiesAsHeaders { get; set; } - /// - /// Gets or sets an array of CloudEvent property names that will be forwarded as Request Headers if is set to true. - /// - /// - /// - /// Note: Setting this will only forwarded the listed property names. - /// - /// - /// ie: ["type", "subject"] - /// - /// - public string[] IncludedCloudEventPropertiesAsHeaders { get; set; } + /// + /// Gets or sets an array of CloudEvent property names that will be forwarded as Request Headers if is set to true. + /// + /// + /// + /// Note: Setting this will only forwarded the listed property names. + /// + /// + /// ie: ["type", "subject"] + /// + /// + public string[] IncludedCloudEventPropertiesAsHeaders { get; set; } - /// - /// Gets or sets an array of CloudEvent property names that will not be forwarded as Request Headers if is set to true. - /// - /// - /// - /// ie: ["type", "subject"] - /// - /// - public string[] ExcludedCloudEventPropertiesFromHeaders { get; set; } - } -} + /// + /// Gets or sets an array of CloudEvent property names that will not be forwarded as Request Headers if is set to true. + /// + /// + /// + /// ie: ["type", "subject"] + /// + /// + public string[] ExcludedCloudEventPropertiesFromHeaders { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprApplicationBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprApplicationBuilderExtensions.cs index 3ad26a4da..742230fd7 100644 --- a/src/Dapr.AspNetCore/DaprApplicationBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprApplicationBuilderExtensions.cs @@ -11,48 +11,47 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.AspNetCore.Builder -{ - using System; - using Dapr; +namespace Microsoft.AspNetCore.Builder; + +using System; +using Dapr; +/// +/// Provides extension methods for . +/// +public static class DaprApplicationBuilderExtensions +{ /// - /// Provides extension methods for . + /// Adds the cloud events middleware to the middleware pipeline. The cloud events middleware will unwrap + /// requests that use the cloud events structured format, allowing the event payload to be read directly. /// - public static class DaprApplicationBuilderExtensions + /// An . + /// The . + public static IApplicationBuilder UseCloudEvents(this IApplicationBuilder builder) { - /// - /// Adds the cloud events middleware to the middleware pipeline. The cloud events middleware will unwrap - /// requests that use the cloud events structured format, allowing the event payload to be read directly. - /// - /// An . - /// The . - public static IApplicationBuilder UseCloudEvents(this IApplicationBuilder builder) + if (builder is null) { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return UseCloudEvents(builder, new CloudEventsMiddlewareOptions()); + throw new ArgumentNullException(nameof(builder)); } - /// - /// Adds the cloud events middleware to the middleware pipeline. The cloud events middleware will unwrap - /// requests that use the cloud events structured format, allowing the event payload to be read directly. - /// - /// An . - /// The to configure optional settings. - /// The . - public static IApplicationBuilder UseCloudEvents(this IApplicationBuilder builder, CloudEventsMiddlewareOptions options) - { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } + return UseCloudEvents(builder, new CloudEventsMiddlewareOptions()); + } - builder.UseMiddleware(options); - return builder; + /// + /// Adds the cloud events middleware to the middleware pipeline. The cloud events middleware will unwrap + /// requests that use the cloud events structured format, allowing the event payload to be read directly. + /// + /// An . + /// The to configure optional settings. + /// The . + public static IApplicationBuilder UseCloudEvents(this IApplicationBuilder builder, CloudEventsMiddlewareOptions options) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); } + + builder.UseMiddleware(options); + return builder; } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs index e55b2916e..9155cd94b 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs @@ -14,38 +14,37 @@ using System; using Dapr.AspNetCore; -namespace Microsoft.AspNetCore.Authentication +namespace Microsoft.AspNetCore.Authentication; + +/// +/// Provides extension methods for . +/// +public static class DaprAuthenticationBuilderExtensions { /// - /// Provides extension methods for . + /// Adds App API token authentication. + /// See https://docs.dapr.io/operations/security/app-api-token/ for more information about App API token authentication in Dapr. + /// By default, the token will be read from the APP_API_TOKEN environment variable. /// - public static class DaprAuthenticationBuilderExtensions - { - /// - /// Adds App API token authentication. - /// See https://docs.dapr.io/operations/security/app-api-token/ for more information about App API token authentication in Dapr. - /// By default, the token will be read from the APP_API_TOKEN environment variable. - /// - /// The . - /// A reference to after the operation has completed. - public static AuthenticationBuilder AddDapr(this AuthenticationBuilder builder) => builder.AddDapr(configureOptions: null); + /// The . + /// A reference to after the operation has completed. + public static AuthenticationBuilder AddDapr(this AuthenticationBuilder builder) => builder.AddDapr(configureOptions: null); - /// - /// Adds App API token authentication. - /// See https://docs.dapr.io/operations/security/app-api-token/ for more information about App API token authentication in Dapr. - /// - /// The . - /// - /// A delegate that allows configuring . - /// By default, the token will be read from the APP_API_TOKEN environment variable. - /// - /// A reference to after the operation has completed. - public static AuthenticationBuilder AddDapr(this AuthenticationBuilder builder, Action configureOptions) - { - return builder - .AddScheme( - DaprAuthenticationOptions.DefaultScheme, - configureOptions); - } + /// + /// Adds App API token authentication. + /// See https://docs.dapr.io/operations/security/app-api-token/ for more information about App API token authentication in Dapr. + /// + /// The . + /// + /// A delegate that allows configuring . + /// By default, the token will be read from the APP_API_TOKEN environment variable. + /// + /// A reference to after the operation has completed. + public static AuthenticationBuilder AddDapr(this AuthenticationBuilder builder, Action configureOptions) + { + return builder + .AddScheme( + DaprAuthenticationOptions.DefaultScheme, + configureOptions); } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs b/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs index dc21b5926..60a2f9744 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs @@ -11,27 +11,27 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore -{ - using System.Collections.Generic; - using System.Security.Claims; - using System.Text.Encodings.Web; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Authentication; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Options; +namespace Dapr.AspNetCore; - internal class DaprAuthenticationHandler : AuthenticationHandler - { - const string DaprApiToken = "Dapr-Api-Token"; +using System.Collections.Generic; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +internal class DaprAuthenticationHandler : AuthenticationHandler +{ + const string DaprApiToken = "Dapr-Api-Token"; #if NET8_0_OR_GREATER - public DaprAuthenticationHandler( - IOptionsMonitor options, - ILoggerFactory logger, - UrlEncoder encoder) : base(options, logger, encoder) - { - } + public DaprAuthenticationHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder) : base(options, logger, encoder) + { + } #else public DaprAuthenticationHandler( IOptionsMonitor options, @@ -42,39 +42,38 @@ public DaprAuthenticationHandler( } #endif - protected override Task HandleAuthenticateAsync() + protected override Task HandleAuthenticateAsync() + { + return Task.FromResult(HandleAuthenticate()); + } + + private AuthenticateResult HandleAuthenticate() + { + if (!Request.Headers.TryGetValue(DaprApiToken, out var token)) { - return Task.FromResult(HandleAuthenticate()); + return AuthenticateResult.NoResult(); } - private AuthenticateResult HandleAuthenticate() + var expectedToken = Options.Token; + if (string.IsNullOrWhiteSpace(expectedToken)) { - if (!Request.Headers.TryGetValue(DaprApiToken, out var token)) - { - return AuthenticateResult.NoResult(); - } - - var expectedToken = Options.Token; - if (string.IsNullOrWhiteSpace(expectedToken)) - { - return AuthenticateResult.Fail("App API Token not configured."); - } + return AuthenticateResult.Fail("App API Token not configured."); + } - if (!string.Equals(token, expectedToken)) - { - return AuthenticateResult.Fail("Not authenticated."); - } + if (!string.Equals(token, expectedToken)) + { + return AuthenticateResult.Fail("Not authenticated."); + } - var claims = new List - { - new Claim(ClaimTypes.Name, "Dapr") - }; - var identity = new ClaimsIdentity(claims, Options.Scheme); - var identities = new List { identity }; - var principal = new ClaimsPrincipal(identities); - var ticket = new AuthenticationTicket(principal, Options.Scheme); + var claims = new List + { + new Claim(ClaimTypes.Name, "Dapr") + }; + var identity = new ClaimsIdentity(claims, Options.Scheme); + var identities = new List { identity }; + var principal = new ClaimsPrincipal(identities); + var ticket = new AuthenticationTicket(principal, Options.Scheme); - return AuthenticateResult.Success(ticket); - } + return AuthenticateResult.Success(ticket); } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs index b12d4d14e..fd9f2794d 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs @@ -13,21 +13,20 @@ using Microsoft.AspNetCore.Authentication; -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// Options class provides information needed to control Dapr Authentication handler behavior. +/// See https://docs.dapr.io/operations/security/app-api-token/ for more information about App API token authentication in Dapr. +/// +public class DaprAuthenticationOptions : AuthenticationSchemeOptions { + internal const string DefaultScheme = "Dapr"; + internal string Scheme { get; } = DefaultScheme; + /// - /// Options class provides information needed to control Dapr Authentication handler behavior. - /// See https://docs.dapr.io/operations/security/app-api-token/ for more information about App API token authentication in Dapr. + /// Gets or sets the App API token. + /// By default, the token will be read from the APP_API_TOKEN environment variable. /// - public class DaprAuthenticationOptions : AuthenticationSchemeOptions - { - internal const string DefaultScheme = "Dapr"; - internal string Scheme { get; } = DefaultScheme; - - /// - /// Gets or sets the App API token. - /// By default, the token will be read from the APP_API_TOKEN environment variable. - /// - public string Token { get; set; } = DaprDefaults.GetDefaultAppApiToken(null); - } -} + public string Token { get; set; } = DaprDefaults.GetDefaultAppApiToken(null); +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprAuthorizationOptionsExtensions.cs b/src/Dapr.AspNetCore/DaprAuthorizationOptionsExtensions.cs index 4fd2c2f24..2074fd501 100644 --- a/src/Dapr.AspNetCore/DaprAuthorizationOptionsExtensions.cs +++ b/src/Dapr.AspNetCore/DaprAuthorizationOptionsExtensions.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.AspNetCore.Authorization -{ - using Dapr.AspNetCore; +namespace Microsoft.AspNetCore.Authorization; + +using Dapr.AspNetCore; +/// +/// Provides extension methods for . +/// +public static class DaprAuthorizationOptionsExtensions +{ /// - /// Provides extension methods for . + /// Adds Dapr authorization policy. /// - public static class DaprAuthorizationOptionsExtensions + /// The . + /// The name of the policy. + public static void AddDapr(this AuthorizationOptions options, string name = "Dapr") { - /// - /// Adds Dapr authorization policy. - /// - /// The . - /// The name of the policy. - public static void AddDapr(this AuthorizationOptions options, string name = "Dapr") + options.AddPolicy(name, policy => { - options.AddPolicy(name, policy => - { - policy.RequireAuthenticatedUser(); - policy.AddAuthenticationSchemes(DaprAuthenticationOptions.DefaultScheme); - }); - } + policy.RequireAuthenticatedUser(); + policy.AddAuthenticationSchemes(DaprAuthenticationOptions.DefaultScheme); + }); } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprEndpointConventionBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprEndpointConventionBuilderExtensions.cs index 0ffaf62d3..88e015acc 100644 --- a/src/Dapr.AspNetCore/DaprEndpointConventionBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprEndpointConventionBuilderExtensions.cs @@ -11,161 +11,160 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.AspNetCore.Builder +namespace Microsoft.AspNetCore.Builder; + +using Dapr; +using Dapr.AspNetCore; +using System; +using System.Collections.Generic; + +/// +/// Contains extension methods for . +/// +public static class DaprEndpointConventionBuilderExtensions { - using Dapr; - using Dapr.AspNetCore; - using System; - using System.Collections.Generic; + /// + /// Adds metadata to the provided . + /// + /// The .\ + /// The name of the pubsub component to use. + /// The topic name. + /// The type. + /// The builder object. + public static T WithTopic(this T builder, string pubsubName, string name) + where T : IEndpointConventionBuilder + { + return WithTopic(builder, pubsubName, name, false); + } /// - /// Contains extension methods for . + /// Adds metadata to the provided . /// - public static class DaprEndpointConventionBuilderExtensions + /// The .\ + /// The name of the pubsub component to use. + /// The topic name. + /// + /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values + /// are determined by the type of pubsub component used. + /// + /// The type. + /// The builder object. + public static T WithTopic(this T builder, string pubsubName, string name, IDictionary metadata) + where T : IEndpointConventionBuilder { - /// - /// Adds metadata to the provided . - /// - /// The .\ - /// The name of the pubsub component to use. - /// The topic name. - /// The type. - /// The builder object. - public static T WithTopic(this T builder, string pubsubName, string name) - where T : IEndpointConventionBuilder - { - return WithTopic(builder, pubsubName, name, false); - } + return WithTopic(builder, pubsubName, name, false, metadata); + } - /// - /// Adds metadata to the provided . - /// - /// The .\ - /// The name of the pubsub component to use. - /// The topic name. - /// - /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values - /// are determined by the type of pubsub component used. - /// - /// The type. - /// The builder object. - public static T WithTopic(this T builder, string pubsubName, string name, IDictionary metadata) - where T : IEndpointConventionBuilder - { - return WithTopic(builder, pubsubName, name, false, metadata); - } + /// + /// Adds metadata to the provided . + /// + /// The .\ + /// The name of the pubsub component to use. + /// The topic name. + /// The enable/disable raw pay load flag. + /// The type. + /// The builder object. + public static T WithTopic(this T builder, string pubsubName, string name, bool enableRawPayload) + where T : IEndpointConventionBuilder + { + return WithTopic(builder, pubsubName, name, enableRawPayload, null); + } - /// - /// Adds metadata to the provided . - /// - /// The .\ - /// The name of the pubsub component to use. - /// The topic name. - /// The enable/disable raw pay load flag. - /// The type. - /// The builder object. - public static T WithTopic(this T builder, string pubsubName, string name, bool enableRawPayload) - where T : IEndpointConventionBuilder + /// + /// Adds metadata to the provided . + /// + /// The .\ + /// The name of the pubsub component to use. + /// The topic name. + /// The enable/disable raw pay load flag. + /// + /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values + /// are determined by the type of pubsub component used. + /// + /// The type. + /// The builder object. + public static T WithTopic(this T builder, string pubsubName, string name, bool enableRawPayload, IDictionary metadata) + where T : IEndpointConventionBuilder + { + if (builder is null) { - return WithTopic(builder, pubsubName, name, enableRawPayload, null); + throw new ArgumentNullException(nameof(builder)); } - /// - /// Adds metadata to the provided . - /// - /// The .\ - /// The name of the pubsub component to use. - /// The topic name. - /// The enable/disable raw pay load flag. - /// - /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values - /// are determined by the type of pubsub component used. - /// - /// The type. - /// The builder object. - public static T WithTopic(this T builder, string pubsubName, string name, bool enableRawPayload, IDictionary metadata) - where T : IEndpointConventionBuilder - { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } + ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - - builder.WithMetadata(new TopicAttribute(pubsubName, name, enableRawPayload)); - if (metadata is not null) + builder.WithMetadata(new TopicAttribute(pubsubName, name, enableRawPayload)); + if (metadata is not null) + { + foreach (var md in metadata) { - foreach (var md in metadata) - { - builder.WithMetadata(new TopicMetadataAttribute(md.Key, md.Value)); - } + builder.WithMetadata(new TopicMetadataAttribute(md.Key, md.Value)); } - return builder; } + return builder; + } - /// - /// Adds metadata to the provided . - /// - /// The .\ - /// The object of TopicOptions class that provides all topic attributes. - /// The type. - /// The builder object. - public static T WithTopic(this T builder, TopicOptions topicOptions) - where T : IEndpointConventionBuilder + /// + /// Adds metadata to the provided . + /// + /// The .\ + /// The object of TopicOptions class that provides all topic attributes. + /// The type. + /// The builder object. + public static T WithTopic(this T builder, TopicOptions topicOptions) + where T : IEndpointConventionBuilder + { + if (builder is null) { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } + throw new ArgumentNullException(nameof(builder)); + } - ArgumentVerifier.ThrowIfNullOrEmpty(topicOptions.PubsubName, nameof(topicOptions.PubsubName)); - ArgumentVerifier.ThrowIfNullOrEmpty(topicOptions.Name, nameof(topicOptions.Name)); + ArgumentVerifier.ThrowIfNullOrEmpty(topicOptions.PubsubName, nameof(topicOptions.PubsubName)); + ArgumentVerifier.ThrowIfNullOrEmpty(topicOptions.Name, nameof(topicOptions.Name)); - var topicObject = new TopicAttribute(topicOptions.PubsubName, topicOptions.Name, topicOptions.DeadLetterTopic, topicOptions.EnableRawPayload); + var topicObject = new TopicAttribute(topicOptions.PubsubName, topicOptions.Name, topicOptions.DeadLetterTopic, topicOptions.EnableRawPayload); - topicObject.Match = topicOptions.Match; - topicObject.Priority = topicOptions.Priority; - topicObject.OwnedMetadatas = topicOptions.OwnedMetadatas; - topicObject.MetadataSeparator = topicObject.MetadataSeparator; + topicObject.Match = topicOptions.Match; + topicObject.Priority = topicOptions.Priority; + topicObject.OwnedMetadatas = topicOptions.OwnedMetadatas; + topicObject.MetadataSeparator = topicObject.MetadataSeparator; - if (topicOptions.Metadata is not null) + if (topicOptions.Metadata is not null) + { + foreach (var md in topicOptions.Metadata) { - foreach (var md in topicOptions.Metadata) - { - builder.WithMetadata(new TopicMetadataAttribute(md.Key, md.Value)); - } + builder.WithMetadata(new TopicMetadataAttribute(md.Key, md.Value)); } + } - builder.WithMetadata(topicObject); + builder.WithMetadata(topicObject); - return builder; - } + return builder; + } - /// - /// Adds metadata to the provided . - /// - /// The .\ - /// The object of BulkSubscribeTopicOptions class that provides - /// all bulk subscribe topic attributes. - /// The type. - /// The builder object. - public static T WithBulkSubscribe(this T builder, BulkSubscribeTopicOptions bulkSubscribeTopicOptions) - where T : IEndpointConventionBuilder + /// + /// Adds metadata to the provided . + /// + /// The .\ + /// The object of BulkSubscribeTopicOptions class that provides + /// all bulk subscribe topic attributes. + /// The type. + /// The builder object. + public static T WithBulkSubscribe(this T builder, BulkSubscribeTopicOptions bulkSubscribeTopicOptions) + where T : IEndpointConventionBuilder + { + if (builder is null) { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } + throw new ArgumentNullException(nameof(builder)); + } - ArgumentVerifier.ThrowIfNullOrEmpty(bulkSubscribeTopicOptions.TopicName, - nameof(bulkSubscribeTopicOptions.TopicName)); + ArgumentVerifier.ThrowIfNullOrEmpty(bulkSubscribeTopicOptions.TopicName, + nameof(bulkSubscribeTopicOptions.TopicName)); - builder.WithMetadata(new BulkSubscribeAttribute(bulkSubscribeTopicOptions.TopicName, - bulkSubscribeTopicOptions.MaxMessagesCount, bulkSubscribeTopicOptions.MaxAwaitDurationMs)); + builder.WithMetadata(new BulkSubscribeAttribute(bulkSubscribeTopicOptions.TopicName, + bulkSubscribeTopicOptions.MaxMessagesCount, bulkSubscribeTopicOptions.MaxAwaitDurationMs)); - return builder; - } + return builder; } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs index 78c453570..9749a87d4 100644 --- a/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs @@ -11,195 +11,194 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.AspNetCore.Builder +namespace Microsoft.AspNetCore.Builder; + +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dapr; +using Dapr.AspNetCore; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Patterns; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +/// +/// Contains extension methods for . +/// +public static class DaprEndpointRouteBuilderExtensions { - using System.Collections.Generic; - using System.Linq; - using System.Text.Json; - using System.Text.Json.Serialization; - using Dapr; - using Dapr.AspNetCore; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Routing; - using Microsoft.AspNetCore.Routing.Patterns; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; + /// + /// Maps an endpoint that will respond to requests to /dapr/subscribe from the + /// Dapr runtime. + /// + /// The . + /// The . + public static IEndpointConventionBuilder MapSubscribeHandler(this IEndpointRouteBuilder endpoints) + { + return CreateSubscribeEndPoint(endpoints); + } /// - /// Contains extension methods for . + /// Maps an endpoint that will respond to requests to /dapr/subscribe from the + /// Dapr runtime. /// - public static class DaprEndpointRouteBuilderExtensions + /// The . + /// Configuration options + /// The . + /// + public static IEndpointConventionBuilder MapSubscribeHandler(this IEndpointRouteBuilder endpoints, SubscribeOptions options) { - /// - /// Maps an endpoint that will respond to requests to /dapr/subscribe from the - /// Dapr runtime. - /// - /// The . - /// The . - public static IEndpointConventionBuilder MapSubscribeHandler(this IEndpointRouteBuilder endpoints) - { - return CreateSubscribeEndPoint(endpoints); - } + return CreateSubscribeEndPoint(endpoints, options); + } - /// - /// Maps an endpoint that will respond to requests to /dapr/subscribe from the - /// Dapr runtime. - /// - /// The . - /// Configuration options - /// The . - /// - public static IEndpointConventionBuilder MapSubscribeHandler(this IEndpointRouteBuilder endpoints, SubscribeOptions options) + private static IEndpointConventionBuilder CreateSubscribeEndPoint(IEndpointRouteBuilder endpoints, SubscribeOptions options = null) + { + if (endpoints is null) { - return CreateSubscribeEndPoint(endpoints, options); + throw new System.ArgumentNullException(nameof(endpoints)); } - private static IEndpointConventionBuilder CreateSubscribeEndPoint(IEndpointRouteBuilder endpoints, SubscribeOptions options = null) + return endpoints.MapGet("dapr/subscribe", async context => { - if (endpoints is null) - { - throw new System.ArgumentNullException(nameof(endpoints)); - } - - return endpoints.MapGet("dapr/subscribe", async context => - { - var logger = context.RequestServices.GetService().CreateLogger("DaprTopicSubscription"); - var dataSource = context.RequestServices.GetRequiredService(); - var subscriptions = dataSource.Endpoints - .OfType() - .Where(e => e.Metadata.GetOrderedMetadata().Any(t => t.Name != null)) // only endpoints which have TopicAttribute with not null Name. - .SelectMany(e => + var logger = context.RequestServices.GetService().CreateLogger("DaprTopicSubscription"); + var dataSource = context.RequestServices.GetRequiredService(); + var subscriptions = dataSource.Endpoints + .OfType() + .Where(e => e.Metadata.GetOrderedMetadata().Any(t => t.Name != null)) // only endpoints which have TopicAttribute with not null Name. + .SelectMany(e => + { + var topicMetadata = e.Metadata.GetOrderedMetadata(); + var originalTopicMetadata = e.Metadata.GetOrderedMetadata(); + var bulkSubscribeMetadata = e.Metadata.GetOrderedMetadata(); + + var subs = new List<(string PubsubName, string Name, string DeadLetterTopic, bool? EnableRawPayload, + string Match, int Priority, Dictionary OriginalTopicMetadata, + string MetadataSeparator, RoutePattern RoutePattern, DaprTopicBulkSubscribe bulkSubscribe)>(); + + for (int i = 0; i < topicMetadata.Count(); i++) { - var topicMetadata = e.Metadata.GetOrderedMetadata(); - var originalTopicMetadata = e.Metadata.GetOrderedMetadata(); - var bulkSubscribeMetadata = e.Metadata.GetOrderedMetadata(); - - var subs = new List<(string PubsubName, string Name, string DeadLetterTopic, bool? EnableRawPayload, - string Match, int Priority, Dictionary OriginalTopicMetadata, - string MetadataSeparator, RoutePattern RoutePattern, DaprTopicBulkSubscribe bulkSubscribe)>(); + DaprTopicBulkSubscribe bulkSubscribe = null; - for (int i = 0; i < topicMetadata.Count(); i++) + foreach (var bulkSubscribeAttr in bulkSubscribeMetadata) { - DaprTopicBulkSubscribe bulkSubscribe = null; - - foreach (var bulkSubscribeAttr in bulkSubscribeMetadata) + if (bulkSubscribeAttr.TopicName != topicMetadata[i].Name) { - if (bulkSubscribeAttr.TopicName != topicMetadata[i].Name) - { - continue; - } - - bulkSubscribe = new DaprTopicBulkSubscribe - { - Enabled = true, - MaxMessagesCount = bulkSubscribeAttr.MaxMessagesCount, - MaxAwaitDurationMs = bulkSubscribeAttr.MaxAwaitDurationMs - }; - break; + continue; } - - subs.Add((topicMetadata[i].PubsubName, - topicMetadata[i].Name, - (topicMetadata[i] as IDeadLetterTopicMetadata)?.DeadLetterTopic, - (topicMetadata[i] as IRawTopicMetadata)?.EnableRawPayload, - topicMetadata[i].Match, - topicMetadata[i].Priority, - originalTopicMetadata.Where(m => (topicMetadata[i] as IOwnedOriginalTopicMetadata)?.OwnedMetadatas?.Any(o => o.Equals(m.Id)) == true || string.IsNullOrEmpty(m.Id)) - .GroupBy(c => c.Name) - .ToDictionary(m => m.Key, m => m.Select(c => c.Value).Distinct().ToArray()), - (topicMetadata[i] as IOwnedOriginalTopicMetadata)?.MetadataSeparator, - e.RoutePattern, - bulkSubscribe)); + + bulkSubscribe = new DaprTopicBulkSubscribe + { + Enabled = true, + MaxMessagesCount = bulkSubscribeAttr.MaxMessagesCount, + MaxAwaitDurationMs = bulkSubscribeAttr.MaxAwaitDurationMs + }; + break; } + + subs.Add((topicMetadata[i].PubsubName, + topicMetadata[i].Name, + (topicMetadata[i] as IDeadLetterTopicMetadata)?.DeadLetterTopic, + (topicMetadata[i] as IRawTopicMetadata)?.EnableRawPayload, + topicMetadata[i].Match, + topicMetadata[i].Priority, + originalTopicMetadata.Where(m => (topicMetadata[i] as IOwnedOriginalTopicMetadata)?.OwnedMetadatas?.Any(o => o.Equals(m.Id)) == true || string.IsNullOrEmpty(m.Id)) + .GroupBy(c => c.Name) + .ToDictionary(m => m.Key, m => m.Select(c => c.Value).Distinct().ToArray()), + (topicMetadata[i] as IOwnedOriginalTopicMetadata)?.MetadataSeparator, + e.RoutePattern, + bulkSubscribe)); + } + + return subs; + }) + .Distinct() + .GroupBy(e => new { e.PubsubName, e.Name }) + .Select(e => e.OrderBy(e => e.Priority)) + .Select(e => + { + var first = e.First(); + var rawPayload = e.Any(e => e.EnableRawPayload.GetValueOrDefault()); + var metadataSeparator = e.FirstOrDefault(e => !string.IsNullOrEmpty(e.MetadataSeparator)).MetadataSeparator ?? ","; + var rules = e.Where(e => !string.IsNullOrEmpty(e.Match)).ToList(); + var defaultRoutes = e.Where(e => string.IsNullOrEmpty(e.Match)).Select(e => RoutePatternToString(e.RoutePattern)).ToList(); + var defaultRoute = defaultRoutes.FirstOrDefault(); + + //multiple identical names. use comma separation. + var metadata = new Metadata(e.SelectMany(c => c.OriginalTopicMetadata).GroupBy(c => c.Key).ToDictionary(c => c.Key, c => string.Join(metadataSeparator, c.SelectMany(c => c.Value).Distinct()))); + if (rawPayload || options?.EnableRawPayload is true) + { + metadata.Add(Metadata.RawPayload, "true"); + } - return subs; - }) - .Distinct() - .GroupBy(e => new { e.PubsubName, e.Name }) - .Select(e => e.OrderBy(e => e.Priority)) - .Select(e => + if (logger != null) { - var first = e.First(); - var rawPayload = e.Any(e => e.EnableRawPayload.GetValueOrDefault()); - var metadataSeparator = e.FirstOrDefault(e => !string.IsNullOrEmpty(e.MetadataSeparator)).MetadataSeparator ?? ","; - var rules = e.Where(e => !string.IsNullOrEmpty(e.Match)).ToList(); - var defaultRoutes = e.Where(e => string.IsNullOrEmpty(e.Match)).Select(e => RoutePatternToString(e.RoutePattern)).ToList(); - var defaultRoute = defaultRoutes.FirstOrDefault(); - - //multiple identical names. use comma separation. - var metadata = new Metadata(e.SelectMany(c => c.OriginalTopicMetadata).GroupBy(c => c.Key).ToDictionary(c => c.Key, c => string.Join(metadataSeparator, c.SelectMany(c => c.Value).Distinct()))); - if (rawPayload || options?.EnableRawPayload is true) + if (defaultRoutes.Count > 1) { - metadata.Add(Metadata.RawPayload, "true"); + logger.LogError("A default subscription to topic {name} on pubsub {pubsub} already exists.", first.Name, first.PubsubName); } - if (logger != null) - { - if (defaultRoutes.Count > 1) - { - logger.LogError("A default subscription to topic {name} on pubsub {pubsub} already exists.", first.Name, first.PubsubName); - } + var duplicatePriorities = rules.GroupBy(e => e.Priority) + .Where(g => g.Count() > 1) + .ToDictionary(x => x.Key, y => y.Count()); - var duplicatePriorities = rules.GroupBy(e => e.Priority) - .Where(g => g.Count() > 1) - .ToDictionary(x => x.Key, y => y.Count()); - - foreach (var entry in duplicatePriorities) - { - logger.LogError("A subscription to topic {name} on pubsub {pubsub} has duplicate priorities for {priority}: found {count} occurrences.", first.Name, first.PubsubName, entry.Key, entry.Value); - } + foreach (var entry in duplicatePriorities) + { + logger.LogError("A subscription to topic {name} on pubsub {pubsub} has duplicate priorities for {priority}: found {count} occurrences.", first.Name, first.PubsubName, entry.Key, entry.Value); } + } - var subscription = new Subscription - { - Topic = first.Name, - PubsubName = first.PubsubName, - Metadata = metadata.Count > 0 ? metadata : null, - BulkSubscribe = first.bulkSubscribe - }; + var subscription = new Subscription + { + Topic = first.Name, + PubsubName = first.PubsubName, + Metadata = metadata.Count > 0 ? metadata : null, + BulkSubscribe = first.bulkSubscribe + }; - if (first.DeadLetterTopic != null) - { - subscription.DeadLetterTopic = first.DeadLetterTopic; - } + if (first.DeadLetterTopic != null) + { + subscription.DeadLetterTopic = first.DeadLetterTopic; + } - // Use the V2 routing rules structure - if (rules.Count > 0) + // Use the V2 routing rules structure + if (rules.Count > 0) + { + subscription.Routes = new Routes { - subscription.Routes = new Routes + Rules = rules.Select(e => new Rule { - Rules = rules.Select(e => new Rule - { - Match = e.Match, - Path = RoutePatternToString(e.RoutePattern), - }).ToList(), - Default = defaultRoute, - }; - } - // Use the V1 structure for backward compatibility. - else - { - subscription.Route = defaultRoute; - } - - return subscription; - }) - .OrderBy(e => (e.PubsubName, e.Topic)); - - await context.Response.WriteAsync(JsonSerializer.Serialize(subscriptions, - new JsonSerializerOptions + Match = e.Match, + Path = RoutePatternToString(e.RoutePattern), + }).ToList(), + Default = defaultRoute, + }; + } + // Use the V1 structure for backward compatibility. + else { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - })); - }); - } + subscription.Route = defaultRoute; + } + + return subscription; + }) + .OrderBy(e => (e.PubsubName, e.Topic)); + + await context.Response.WriteAsync(JsonSerializer.Serialize(subscriptions, + new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + })); + }); + } - private static string RoutePatternToString(RoutePattern routePattern) - { - return string.Join("/", routePattern.PathSegments - .Select(segment => string.Concat(segment.Parts.Cast() - .Select(part => part.Content)))); - } + private static string RoutePatternToString(RoutePattern routePattern) + { + return string.Join("/", routePattern.PathSegments + .Select(segment => string.Concat(segment.Parts.Cast() + .Select(part => part.Content)))); } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs index 6209fea5a..1309c78d4 100644 --- a/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs @@ -11,47 +11,46 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.Extensions.DependencyInjection -{ - using System; - using System.Linq; - using Dapr.AspNetCore; - using Dapr.Client; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.ApplicationModels; - using Microsoft.Extensions.DependencyInjection.Extensions; +namespace Microsoft.Extensions.DependencyInjection; + +using System; +using System.Linq; +using Dapr.AspNetCore; +using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.Extensions.DependencyInjection.Extensions; +/// +/// Provides extension methods for . +/// +public static class DaprMvcBuilderExtensions +{ /// - /// Provides extension methods for . + /// Adds Dapr integration for MVC to the provided . /// - public static class DaprMvcBuilderExtensions + /// The . + /// The (optional) to use for configuring the DaprClient. + /// The builder. + public static IMvcBuilder AddDapr(this IMvcBuilder builder, Action configureClient = null) { - /// - /// Adds Dapr integration for MVC to the provided . - /// - /// The . - /// The (optional) to use for configuring the DaprClient. - /// The builder. - public static IMvcBuilder AddDapr(this IMvcBuilder builder, Action configureClient = null) + if (builder is null) { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } + throw new ArgumentNullException(nameof(builder)); + } - builder.Services.AddDaprClient(configureClient); + builder.Services.AddDaprClient(configureClient); - builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); - builder.Services.Configure(options => + builder.Services.Configure(options => + { + if (!options.ModelBinderProviders.Any(p => p is StateEntryModelBinderProvider)) { - if (!options.ModelBinderProviders.Any(p => p is StateEntryModelBinderProvider)) - { - options.ModelBinderProviders.Insert(0, new StateEntryModelBinderProvider()); - } - }); + options.ModelBinderProviders.Insert(0, new StateEntryModelBinderProvider()); + } + }); - return builder; - } + return builder; } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/FromStateAttribute.cs b/src/Dapr.AspNetCore/FromStateAttribute.cs index 16299ba07..cbec456e8 100644 --- a/src/Dapr.AspNetCore/FromStateAttribute.cs +++ b/src/Dapr.AspNetCore/FromStateAttribute.cs @@ -11,62 +11,61 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.AspNetCore.Mvc -{ - using System; - using Dapr; - using Microsoft.AspNetCore.Mvc.ModelBinding; +namespace Microsoft.AspNetCore.Mvc; + +using System; +using Dapr; +using Microsoft.AspNetCore.Mvc.ModelBinding; +/// +/// Attributes a parameter or property as retrieved from the Dapr state store. +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] +public class FromStateAttribute : Attribute, IBindingSourceMetadata +{ /// - /// Attributes a parameter or property as retrieved from the Dapr state store. + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] - public class FromStateAttribute : Attribute, IBindingSourceMetadata + /// The state store name. + public FromStateAttribute(string storeName) { - /// - /// Initializes a new instance of the class. - /// - /// The state store name. - public FromStateAttribute(string storeName) + if (string.IsNullOrEmpty(storeName)) { - if (string.IsNullOrEmpty(storeName)) - { - throw new ArgumentException("The value cannot be null or empty.", nameof(storeName)); - } - - this.StoreName = storeName; + throw new ArgumentException("The value cannot be null or empty.", nameof(storeName)); } - /// - /// Initializes a new instance of the class. - /// - /// The state store name. - /// The state key. - public FromStateAttribute(string storeName, string key) - { - this.StoreName = storeName; - this.Key = key; - } + this.StoreName = storeName; + } - /// - /// Gets the state store name. - /// - public string StoreName { get; } + /// + /// Initializes a new instance of the class. + /// + /// The state store name. + /// The state key. + public FromStateAttribute(string storeName, string key) + { + this.StoreName = storeName; + this.Key = key; + } - /// - /// Gets the state store key. - /// - public string Key { get; } + /// + /// Gets the state store name. + /// + public string StoreName { get; } - /// - /// Gets the . - /// - public BindingSource BindingSource + /// + /// Gets the state store key. + /// + public string Key { get; } + + /// + /// Gets the . + /// + public BindingSource BindingSource + { + get { - get - { - return new FromStateBindingSource(this.StoreName, this.Key); - } + return new FromStateBindingSource(this.StoreName, this.Key); } } } \ No newline at end of file diff --git a/src/Dapr.AspNetCore/FromStateBindingSource.cs b/src/Dapr.AspNetCore/FromStateBindingSource.cs index 16837de75..997e53bcf 100644 --- a/src/Dapr.AspNetCore/FromStateBindingSource.cs +++ b/src/Dapr.AspNetCore/FromStateBindingSource.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr -{ - using Microsoft.AspNetCore.Mvc.ModelBinding; +namespace Dapr; + +using Microsoft.AspNetCore.Mvc.ModelBinding; - internal class FromStateBindingSource : BindingSource +internal class FromStateBindingSource : BindingSource +{ + public FromStateBindingSource(string storeName, string key) + : base("state", "Dapr state store", isGreedy: true, isFromRequest: false) { - public FromStateBindingSource(string storeName, string key) - : base("state", "Dapr state store", isGreedy: true, isFromRequest: false) - { - this.StoreName = storeName; - this.Key = key; - } + this.StoreName = storeName; + this.Key = key; + } - public string StoreName { get; } + public string StoreName { get; } - public string Key { get; } - } + public string Key { get; } } \ No newline at end of file diff --git a/src/Dapr.AspNetCore/IBulkSubscribeMetadata.cs b/src/Dapr.AspNetCore/IBulkSubscribeMetadata.cs index 1e4fd006d..68c2b04d6 100644 --- a/src/Dapr.AspNetCore/IBulkSubscribeMetadata.cs +++ b/src/Dapr.AspNetCore/IBulkSubscribeMetadata.cs @@ -1,24 +1,23 @@ -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +/// +/// Bulk Subscribe Metadata that describes bulk subscribe configuration options. +/// +public interface IBulkSubscribeMetadata { /// - /// Bulk Subscribe Metadata that describes bulk subscribe configuration options. + /// Gets the maximum number of messages in a bulk message from the message bus. /// - public interface IBulkSubscribeMetadata - { - /// - /// Gets the maximum number of messages in a bulk message from the message bus. - /// - int MaxMessagesCount { get; } + int MaxMessagesCount { get; } - /// - /// Gets the Maximum duration to wait for maxBulkSubCount messages by the message bus - /// before sending the messages to Dapr. - /// - int MaxAwaitDurationMs { get; } + /// + /// Gets the Maximum duration to wait for maxBulkSubCount messages by the message bus + /// before sending the messages to Dapr. + /// + int MaxAwaitDurationMs { get; } - /// - /// The name of the topic to be bulk subscribed. - /// - public string TopicName { get; } - } -} + /// + /// The name of the topic to be bulk subscribed. + /// + public string TopicName { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/IDeadLetterTopicMetadata.cs b/src/Dapr.AspNetCore/IDeadLetterTopicMetadata.cs index 97707b980..85227e4fe 100644 --- a/src/Dapr.AspNetCore/IDeadLetterTopicMetadata.cs +++ b/src/Dapr.AspNetCore/IDeadLetterTopicMetadata.cs @@ -11,17 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// IDeadLetterTopicMetadata that describes the metadata of a dead letter topic. +/// +public interface IDeadLetterTopicMetadata { /// - /// IDeadLetterTopicMetadata that describes the metadata of a dead letter topic. + /// Gets the dead letter topic name /// - public interface IDeadLetterTopicMetadata - { - /// - /// Gets the dead letter topic name - /// - public string DeadLetterTopic { get; } - } -} - + public string DeadLetterTopic { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/IOriginalTopicMetadata.cs b/src/Dapr.AspNetCore/IOriginalTopicMetadata.cs index 71aa952c3..4d7df6a2c 100644 --- a/src/Dapr.AspNetCore/IOriginalTopicMetadata.cs +++ b/src/Dapr.AspNetCore/IOriginalTopicMetadata.cs @@ -11,30 +11,29 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// IOriginalTopicMetadata that describes subscribe endpoint to a topic original metadata. +/// +public interface IOriginalTopicMetadata { /// - /// IOriginalTopicMetadata that describes subscribe endpoint to a topic original metadata. + /// Gets the topic metadata id. /// - public interface IOriginalTopicMetadata - { - /// - /// Gets the topic metadata id. - /// - /// - /// It is only used for simple identification,. When it is empty, it can be used for all topics in the current context. - /// - string Id { get; } + /// + /// It is only used for simple identification,. When it is empty, it can be used for all topics in the current context. + /// + string Id { get; } - /// - /// Gets the topic metadata name. - /// - /// Multiple identical names. only the first is valid. - string Name { get; } + /// + /// Gets the topic metadata name. + /// + /// Multiple identical names. only the first is valid. + string Name { get; } - /// - /// Gets the topic metadata value. - /// - string Value { get; } - } -} + /// + /// Gets the topic metadata value. + /// + string Value { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/IOwnedOriginalTopicMetadata.cs b/src/Dapr.AspNetCore/IOwnedOriginalTopicMetadata.cs index 36ae45e5d..378e0f1ce 100644 --- a/src/Dapr.AspNetCore/IOwnedOriginalTopicMetadata.cs +++ b/src/Dapr.AspNetCore/IOwnedOriginalTopicMetadata.cs @@ -11,23 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// IOwnedOriginalTopicMetadata that describes subscribe endpoint to topic owned metadata. +/// +public interface IOwnedOriginalTopicMetadata { /// - /// IOwnedOriginalTopicMetadata that describes subscribe endpoint to topic owned metadata. + /// Gets the owned by topic. /// - public interface IOwnedOriginalTopicMetadata - { - /// - /// Gets the owned by topic. - /// - /// When the is not empty, the metadata owned by topic. - string[] OwnedMetadatas { get; } + /// When the is not empty, the metadata owned by topic. + string[] OwnedMetadatas { get; } - /// - /// Get separator to use for metadata - /// - /// Separator to be used for when multiple values exist for a . - string MetadataSeparator { get; } - } -} + /// + /// Get separator to use for metadata + /// + /// Separator to be used for when multiple values exist for a . + string MetadataSeparator { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/IRawTopicMetadata.cs b/src/Dapr.AspNetCore/IRawTopicMetadata.cs index 09b682f5e..88a6f589d 100644 --- a/src/Dapr.AspNetCore/IRawTopicMetadata.cs +++ b/src/Dapr.AspNetCore/IRawTopicMetadata.cs @@ -11,16 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// RawMetadata that describes subscribe endpoint to enable or disable processing raw messages. +/// +public interface IRawTopicMetadata { /// - /// RawMetadata that describes subscribe endpoint to enable or disable processing raw messages. + /// Gets the enable or disable value for processing raw messages. /// - public interface IRawTopicMetadata - { - /// - /// Gets the enable or disable value for processing raw messages. - /// - bool? EnableRawPayload { get; } - } -} + bool? EnableRawPayload { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/ITopicMetadata.cs b/src/Dapr.AspNetCore/ITopicMetadata.cs index eb3732139..81e0459c5 100644 --- a/src/Dapr.AspNetCore/ITopicMetadata.cs +++ b/src/Dapr.AspNetCore/ITopicMetadata.cs @@ -11,31 +11,30 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// ITopicMetadata that describes an endpoint as a subscriber to a topic. +/// +public interface ITopicMetadata { /// - /// ITopicMetadata that describes an endpoint as a subscriber to a topic. + /// Gets the topic name. /// - public interface ITopicMetadata - { - /// - /// Gets the topic name. - /// - string Name { get; } + string Name { get; } - /// - /// Gets the pubsub component name name. - /// - string PubsubName { get; } + /// + /// Gets the pubsub component name name. + /// + string PubsubName { get; } - /// - /// The CEL expression to use to match events for this handler. - /// - string Match { get; } + /// + /// The CEL expression to use to match events for this handler. + /// + string Match { get; } - /// - /// The priority in which this rule should be evaluated (lower to higher). - /// - int Priority { get; } - } -} + /// + /// The priority in which this rule should be evaluated (lower to higher). + /// + int Priority { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/StateEntryApplicationModelProvider.cs b/src/Dapr.AspNetCore/StateEntryApplicationModelProvider.cs index 4760786f6..53df04000 100644 --- a/src/Dapr.AspNetCore/StateEntryApplicationModelProvider.cs +++ b/src/Dapr.AspNetCore/StateEntryApplicationModelProvider.cs @@ -11,65 +11,64 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +using System; +using Dapr.AspNetCore.Resources; +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +internal class StateEntryApplicationModelProvider : IApplicationModelProvider { - using System; - using Dapr.AspNetCore.Resources; - using Microsoft.AspNetCore.Mvc.ApplicationModels; + public int Order => 0; - internal class StateEntryApplicationModelProvider : IApplicationModelProvider + public void OnProvidersExecuting(ApplicationModelProviderContext context) { - public int Order => 0; + } - public void OnProvidersExecuting(ApplicationModelProviderContext context) + public void OnProvidersExecuted(ApplicationModelProviderContext context) + { + // Run after default providers, and customize the binding source for StateEntry<>. + foreach (var controller in context.Result.Controllers) { - } + foreach (var property in controller.ControllerProperties) + { + if (property.BindingInfo == null) + { + // Not bindable. + } + else if (property.BindingInfo.BindingSource?.Id == "state") + { + // Already configured, don't overwrite in case the user customized it. + } + else if (IsStateEntryType(property.ParameterType)) + { + throw new InvalidOperationException(SR.ErrorStateStoreNameNotProvidedForStateEntry); + } + } - public void OnProvidersExecuted(ApplicationModelProviderContext context) - { - // Run after default providers, and customize the binding source for StateEntry<>. - foreach (var controller in context.Result.Controllers) + foreach (var action in controller.Actions) { - foreach (var property in controller.ControllerProperties) + foreach (var parameter in action.Parameters) { - if (property.BindingInfo == null) + if (parameter.BindingInfo == null) { // Not bindable. } - else if (property.BindingInfo.BindingSource?.Id == "state") + else if (parameter.BindingInfo.BindingSource?.Id == "state") { // Already configured, don't overwrite in case the user customized it. } - else if (IsStateEntryType(property.ParameterType)) + else if (IsStateEntryType(parameter.ParameterType)) { throw new InvalidOperationException(SR.ErrorStateStoreNameNotProvidedForStateEntry); } } - - foreach (var action in controller.Actions) - { - foreach (var parameter in action.Parameters) - { - if (parameter.BindingInfo == null) - { - // Not bindable. - } - else if (parameter.BindingInfo.BindingSource?.Id == "state") - { - // Already configured, don't overwrite in case the user customized it. - } - else if (IsStateEntryType(parameter.ParameterType)) - { - throw new InvalidOperationException(SR.ErrorStateStoreNameNotProvidedForStateEntry); - } - } - } } } + } - private static bool IsStateEntryType(Type type) - { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(StateEntry<>); - } + private static bool IsStateEntryType(Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(StateEntry<>); } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/StateEntryModelBinder.cs b/src/Dapr.AspNetCore/StateEntryModelBinder.cs index abc01d7f8..34bc40d6e 100644 --- a/src/Dapr.AspNetCore/StateEntryModelBinder.cs +++ b/src/Dapr.AspNetCore/StateEntryModelBinder.cs @@ -11,102 +11,101 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore +namespace Dapr.AspNetCore; + +using System; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.DependencyInjection; +using Dapr.Client; + +internal class StateEntryModelBinder : IModelBinder { - using System; - using System.Reflection; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Mvc.ModelBinding; - using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; - using Microsoft.Extensions.DependencyInjection; - using Dapr.Client; + private readonly Func> thunk; + private readonly string key; + private readonly string storeName; + private readonly bool isStateEntry; + private readonly Type type; - internal class StateEntryModelBinder : IModelBinder + public StateEntryModelBinder(string storeName, string key, bool isStateEntry, Type type) { - private readonly Func> thunk; - private readonly string key; - private readonly string storeName; - private readonly bool isStateEntry; - private readonly Type type; + this.storeName = storeName; + this.key = key; + this.isStateEntry = isStateEntry; + this.type = type; - public StateEntryModelBinder(string storeName, string key, bool isStateEntry, Type type) + if (isStateEntry) { - this.storeName = storeName; - this.key = key; - this.isStateEntry = isStateEntry; - this.type = type; - - if (isStateEntry) - { - var method = this.GetType().GetMethod(nameof(GetStateEntryAsync), BindingFlags.Static | BindingFlags.NonPublic); - method = method.MakeGenericMethod(type); - this.thunk = (Func>)Delegate.CreateDelegate(typeof(Func>), null, method); - } - else - { - var method = this.GetType().GetMethod(nameof(GetStateAsync), BindingFlags.Static | BindingFlags.NonPublic); - method = method.MakeGenericMethod(type); - this.thunk = (Func>)Delegate.CreateDelegate(typeof(Func>), null, method); - } + var method = this.GetType().GetMethod(nameof(GetStateEntryAsync), BindingFlags.Static | BindingFlags.NonPublic); + method = method.MakeGenericMethod(type); + this.thunk = (Func>)Delegate.CreateDelegate(typeof(Func>), null, method); } - - public async Task BindModelAsync(ModelBindingContext bindingContext) + else { - if (bindingContext is null) - { - throw new ArgumentNullException(nameof(bindingContext)); - } + var method = this.GetType().GetMethod(nameof(GetStateAsync), BindingFlags.Static | BindingFlags.NonPublic); + method = method.MakeGenericMethod(type); + this.thunk = (Func>)Delegate.CreateDelegate(typeof(Func>), null, method); + } + } - var daprClient = bindingContext.HttpContext.RequestServices.GetRequiredService(); + public async Task BindModelAsync(ModelBindingContext bindingContext) + { + if (bindingContext is null) + { + throw new ArgumentNullException(nameof(bindingContext)); + } - // Look up route values to use for keys into state. - bool missingKey = false; - var keyName = this.key ?? bindingContext.FieldName; - var key = (string)bindingContext.HttpContext.Request.RouteValues[keyName]; - if (string.IsNullOrEmpty(key)) - { - missingKey = true; - } + var daprClient = bindingContext.HttpContext.RequestServices.GetRequiredService(); - if (missingKey) - { - // If we get here this is a configuration error. The error is somewhat opaque on - // purpose to avoid leaking too much information about the app. - var message = $"Required value {keyName} not present."; - bindingContext.Result = ModelBindingResult.Failed(); - bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, message); - return; - } - - var obj = await this.thunk(daprClient, this.storeName, key); - - // When the state isn't found in the state store: - // - If the StateEntryModelBinder is associated with a value of type StateEntry, then the above call returns an object of type - // StateEntry which is non-null, but StateEntry.Value is null - // - If the StateEntryModelBinder is associated with a value of type T, then the above call returns a null value. - if (obj == null) - { - bindingContext.Result = ModelBindingResult.Failed(); - } - else - { - bindingContext.Result = ModelBindingResult.Success(obj); - bindingContext.ValidationState.Add(bindingContext.Result.Model, new ValidationStateEntry() - { - // Don't do validation since the data came from a trusted source. - SuppressValidation = true, - }); - } + // Look up route values to use for keys into state. + bool missingKey = false; + var keyName = this.key ?? bindingContext.FieldName; + var key = (string)bindingContext.HttpContext.Request.RouteValues[keyName]; + if (string.IsNullOrEmpty(key)) + { + missingKey = true; } - private static async Task GetStateEntryAsync(DaprClient daprClient, string storeName, string key) + if (missingKey) { - return await daprClient.GetStateEntryAsync(storeName, key); + // If we get here this is a configuration error. The error is somewhat opaque on + // purpose to avoid leaking too much information about the app. + var message = $"Required value {keyName} not present."; + bindingContext.Result = ModelBindingResult.Failed(); + bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, message); + return; } + + var obj = await this.thunk(daprClient, this.storeName, key); - private static async Task GetStateAsync(DaprClient daprClient, string storeName, string key) + // When the state isn't found in the state store: + // - If the StateEntryModelBinder is associated with a value of type StateEntry, then the above call returns an object of type + // StateEntry which is non-null, but StateEntry.Value is null + // - If the StateEntryModelBinder is associated with a value of type T, then the above call returns a null value. + if (obj == null) + { + bindingContext.Result = ModelBindingResult.Failed(); + } + else { - return await daprClient.GetStateAsync(storeName, key); + bindingContext.Result = ModelBindingResult.Success(obj); + bindingContext.ValidationState.Add(bindingContext.Result.Model, new ValidationStateEntry() + { + // Don't do validation since the data came from a trusted source. + SuppressValidation = true, + }); } } -} + + private static async Task GetStateEntryAsync(DaprClient daprClient, string storeName, string key) + { + return await daprClient.GetStateEntryAsync(storeName, key); + } + + private static async Task GetStateAsync(DaprClient daprClient, string storeName, string key) + { + return await daprClient.GetStateAsync(storeName, key); + } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/StateEntryModelBinderProvider.cs b/src/Dapr.AspNetCore/StateEntryModelBinderProvider.cs index 8aeadfc21..620df0fca 100644 --- a/src/Dapr.AspNetCore/StateEntryModelBinderProvider.cs +++ b/src/Dapr.AspNetCore/StateEntryModelBinderProvider.cs @@ -11,52 +11,51 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore -{ - using System; - using Microsoft.AspNetCore.Mvc.ModelBinding; +namespace Dapr.AspNetCore; + +using System; +using Microsoft.AspNetCore.Mvc.ModelBinding; - internal class StateEntryModelBinderProvider : IModelBinderProvider +internal class StateEntryModelBinderProvider : IModelBinderProvider +{ + public IModelBinder GetBinder(ModelBinderProviderContext context) { - public IModelBinder GetBinder(ModelBinderProviderContext context) + if (context is null) { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (!CanBind(context, out var type)) - { - return null; - } - - var storename = (context.BindingInfo.BindingSource as FromStateBindingSource)?.StoreName; - var key = (context.BindingInfo.BindingSource as FromStateBindingSource)?.Key; - return new StateEntryModelBinder(storename, key, type != context.Metadata.ModelType, type); + throw new ArgumentNullException(nameof(context)); } - private static bool CanBind(ModelBinderProviderContext context, out Type type) + if (!CanBind(context, out var type)) { - if (context.BindingInfo.BindingSource?.Id == "state") - { - // [FromState] - type = Unwrap(context.Metadata.ModelType); - return true; - } - - type = null; - return false; + return null; } - private static Type Unwrap(Type type) + var storename = (context.BindingInfo.BindingSource as FromStateBindingSource)?.StoreName; + var key = (context.BindingInfo.BindingSource as FromStateBindingSource)?.Key; + return new StateEntryModelBinder(storename, key, type != context.Metadata.ModelType, type); + } + + private static bool CanBind(ModelBinderProviderContext context, out Type type) + { + if (context.BindingInfo.BindingSource?.Id == "state") { - if (type.IsGenericType && - type.GetGenericTypeDefinition() == typeof(StateEntry<>)) - { - return type.GetGenericArguments()[0]; - } + // [FromState] + type = Unwrap(context.Metadata.ModelType); + return true; + } + + type = null; + return false; + } - return type; + private static Type Unwrap(Type type) + { + if (type.IsGenericType && + type.GetGenericTypeDefinition() == typeof(StateEntry<>)) + { + return type.GetGenericArguments()[0]; } + + return type; } -} +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/SubscribeOptions.cs b/src/Dapr.AspNetCore/SubscribeOptions.cs index eff8a66cc..318af15d9 100644 --- a/src/Dapr.AspNetCore/SubscribeOptions.cs +++ b/src/Dapr.AspNetCore/SubscribeOptions.cs @@ -11,16 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// This class defines configurations for the subscribe endpoint. +/// +public class SubscribeOptions { /// - /// This class defines configurations for the subscribe endpoint. + /// Gets or Sets a value which indicates whether to enable or disable processing raw messages. /// - public class SubscribeOptions - { - /// - /// Gets or Sets a value which indicates whether to enable or disable processing raw messages. - /// - public bool EnableRawPayload { get; set; } - } -} + public bool EnableRawPayload { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/Subscription.cs b/src/Dapr.AspNetCore/Subscription.cs index 9fcf237af..7b0e127b4 100644 --- a/src/Dapr.AspNetCore/Subscription.cs +++ b/src/Dapr.AspNetCore/Subscription.cs @@ -13,106 +13,105 @@ using System.Collections.Generic; -namespace Dapr +namespace Dapr; + +/// +/// This class defines subscribe endpoint response +/// +internal class Subscription { /// - /// This class defines subscribe endpoint response + /// Gets or sets the topic name. /// - internal class Subscription - { - /// - /// Gets or sets the topic name. - /// - public string Topic { get; set; } - - /// - /// Gets or sets the pubsub name - /// - public string PubsubName { get; set; } - - /// - /// Gets or sets the route - /// - public string Route { get; set; } - - /// - /// Gets or sets the routes - /// - public Routes Routes { get; set; } - - /// - /// Gets or sets the metadata. - /// - public Metadata Metadata { get; set; } - - /// - /// Gets or sets the deadletter topic. - /// - public string DeadLetterTopic { get; set; } + public string Topic { get; set; } + + /// + /// Gets or sets the pubsub name + /// + public string PubsubName { get; set; } + + /// + /// Gets or sets the route + /// + public string Route { get; set; } - /// - /// Gets or sets the bulk subscribe options. - /// - public DaprTopicBulkSubscribe BulkSubscribe { get; set; } - } + /// + /// Gets or sets the routes + /// + public Routes Routes { get; set; } /// - /// This class defines the metadata for subscribe endpoint. + /// Gets or sets the metadata. /// - internal class Metadata : Dictionary - { - public Metadata() { } - - public Metadata(IDictionary dictionary) : base(dictionary) { } - - /// - /// RawPayload key - /// - internal const string RawPayload = "rawPayload"; - } - - internal class Routes - { - /// - /// Gets or sets the default route - /// - public string Default { get; set; } - - /// - /// Gets or sets the routing rules - /// - public List Rules { get; set; } - } - - internal class Rule - { - /// - /// Gets or sets the CEL expression to match this route. - /// - public string Match { get; set; } - - /// - /// Gets or sets the path of the route. - /// - public string Path { get; set; } - } - - internal class DaprTopicBulkSubscribe - { - /// - /// Gets or sets whether bulk subscribe option is enabled for a topic. - /// - public bool Enabled { get; set; } - - /// - /// Gets or sets the maximum number of messages in a bulk message from the message bus. - /// - public int MaxMessagesCount { get; set; } + public Metadata Metadata { get; set; } - /// - /// Gets or sets the Maximum duration to wait for maxBulkSubCount messages by the message bus - /// before sending the messages to Dapr. - /// - public int MaxAwaitDurationMs { get; set; } - } + /// + /// Gets or sets the deadletter topic. + /// + public string DeadLetterTopic { get; set; } + + /// + /// Gets or sets the bulk subscribe options. + /// + public DaprTopicBulkSubscribe BulkSubscribe { get; set; } +} + +/// +/// This class defines the metadata for subscribe endpoint. +/// +internal class Metadata : Dictionary +{ + public Metadata() { } + + public Metadata(IDictionary dictionary) : base(dictionary) { } + + /// + /// RawPayload key + /// + internal const string RawPayload = "rawPayload"; +} + +internal class Routes +{ + /// + /// Gets or sets the default route + /// + public string Default { get; set; } + + /// + /// Gets or sets the routing rules + /// + public List Rules { get; set; } +} + +internal class Rule +{ + /// + /// Gets or sets the CEL expression to match this route. + /// + public string Match { get; set; } + + /// + /// Gets or sets the path of the route. + /// + public string Path { get; set; } } + +internal class DaprTopicBulkSubscribe +{ + /// + /// Gets or sets whether bulk subscribe option is enabled for a topic. + /// + public bool Enabled { get; set; } + + /// + /// Gets or sets the maximum number of messages in a bulk message from the message bus. + /// + public int MaxMessagesCount { get; set; } + + /// + /// Gets or sets the Maximum duration to wait for maxBulkSubCount messages by the message bus + /// before sending the messages to Dapr. + /// + public int MaxAwaitDurationMs { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/TopicAttribute.cs b/src/Dapr.AspNetCore/TopicAttribute.cs index daa5d850f..1d1f7a1ee 100644 --- a/src/Dapr.AspNetCore/TopicAttribute.cs +++ b/src/Dapr.AspNetCore/TopicAttribute.cs @@ -11,144 +11,143 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +using System; + +/// +/// TopicAttribute describes an endpoint as a subscriber to a topic. +/// +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +public class TopicAttribute : Attribute, ITopicMetadata, IRawTopicMetadata, IOwnedOriginalTopicMetadata, IDeadLetterTopicMetadata { - using System; + /// + /// Initializes a new instance of the class. + /// + /// The name of the pubsub component to use. + /// The topic name. + /// The topic owned metadata ids. + /// Separator to use for metadata. + public TopicAttribute(string pubsubName, string name, string[] ownedMetadatas = null, string metadataSeparator = null) + { + ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); + + this.Name = name; + this.PubsubName = pubsubName; + this.OwnedMetadatas = ownedMetadatas; + this.MetadataSeparator = metadataSeparator; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the pubsub component to use. + /// The topic name. + /// The enable/disable raw pay load flag. + /// The topic owned metadata ids. + /// Separator to use for metadata. + public TopicAttribute(string pubsubName, string name, bool enableRawPayload, string[] ownedMetadatas = null, string metadataSeparator = null) + { + ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); + + this.Name = name; + this.PubsubName = pubsubName; + this.EnableRawPayload = enableRawPayload; + this.OwnedMetadatas = ownedMetadatas; + this.MetadataSeparator = metadataSeparator; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the pubsub component to use. + /// The topic name. + /// The CEL expression to test the cloud event with. + /// The priority of the rule (low-to-high values). + /// The topic owned metadata ids. + /// Separator to use for metadata. + public TopicAttribute(string pubsubName, string name, string match, int priority, string[] ownedMetadatas = null, string metadataSeparator = null) + { + ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); + + this.Name = name; + this.PubsubName = pubsubName; + this.Match = match; + this.Priority = priority; + this.OwnedMetadatas = ownedMetadatas; + this.MetadataSeparator = metadataSeparator; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the pubsub component to use. + /// The topic name. + /// The enable/disable raw pay load flag. + /// The CEL expression to test the cloud event with. + /// The priority of the rule (low-to-high values). + /// The topic owned metadata ids. + /// Separator to use for metadata. + public TopicAttribute(string pubsubName, string name, bool enableRawPayload, string match, int priority, string[] ownedMetadatas = null, string metadataSeparator = null) + { + ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); + + this.Name = name; + this.PubsubName = pubsubName; + this.EnableRawPayload = enableRawPayload; + this.Match = match; + this.Priority = priority; + this.OwnedMetadatas = ownedMetadatas; + this.MetadataSeparator = metadataSeparator; + } /// - /// TopicAttribute describes an endpoint as a subscriber to a topic. + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class TopicAttribute : Attribute, ITopicMetadata, IRawTopicMetadata, IOwnedOriginalTopicMetadata, IDeadLetterTopicMetadata + /// The name of the pubsub component to use. + /// The topic name. + /// The dead letter topic name. + /// The enable/disable raw pay load flag. + /// The topic owned metadata ids. + /// Separator to use for metadata. + public TopicAttribute(string pubsubName, string name, string deadLetterTopic, bool enableRawPayload, string[] ownedMetadatas = null, string metadataSeparator = null) { - /// - /// Initializes a new instance of the class. - /// - /// The name of the pubsub component to use. - /// The topic name. - /// The topic owned metadata ids. - /// Separator to use for metadata. - public TopicAttribute(string pubsubName, string name, string[] ownedMetadatas = null, string metadataSeparator = null) - { - ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - - this.Name = name; - this.PubsubName = pubsubName; - this.OwnedMetadatas = ownedMetadatas; - this.MetadataSeparator = metadataSeparator; - } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the pubsub component to use. - /// The topic name. - /// The enable/disable raw pay load flag. - /// The topic owned metadata ids. - /// Separator to use for metadata. - public TopicAttribute(string pubsubName, string name, bool enableRawPayload, string[] ownedMetadatas = null, string metadataSeparator = null) - { - ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - - this.Name = name; - this.PubsubName = pubsubName; - this.EnableRawPayload = enableRawPayload; - this.OwnedMetadatas = ownedMetadatas; - this.MetadataSeparator = metadataSeparator; - } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the pubsub component to use. - /// The topic name. - /// The CEL expression to test the cloud event with. - /// The priority of the rule (low-to-high values). - /// The topic owned metadata ids. - /// Separator to use for metadata. - public TopicAttribute(string pubsubName, string name, string match, int priority, string[] ownedMetadatas = null, string metadataSeparator = null) - { - ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - - this.Name = name; - this.PubsubName = pubsubName; - this.Match = match; - this.Priority = priority; - this.OwnedMetadatas = ownedMetadatas; - this.MetadataSeparator = metadataSeparator; - } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the pubsub component to use. - /// The topic name. - /// The enable/disable raw pay load flag. - /// The CEL expression to test the cloud event with. - /// The priority of the rule (low-to-high values). - /// The topic owned metadata ids. - /// Separator to use for metadata. - public TopicAttribute(string pubsubName, string name, bool enableRawPayload, string match, int priority, string[] ownedMetadatas = null, string metadataSeparator = null) - { - ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - - this.Name = name; - this.PubsubName = pubsubName; - this.EnableRawPayload = enableRawPayload; - this.Match = match; - this.Priority = priority; - this.OwnedMetadatas = ownedMetadatas; - this.MetadataSeparator = metadataSeparator; - } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the pubsub component to use. - /// The topic name. - /// The dead letter topic name. - /// The enable/disable raw pay load flag. - /// The topic owned metadata ids. - /// Separator to use for metadata. - public TopicAttribute(string pubsubName, string name, string deadLetterTopic, bool enableRawPayload, string[] ownedMetadatas = null, string metadataSeparator = null) - { - ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - - this.Name = name; - this.PubsubName = pubsubName; - this.DeadLetterTopic = deadLetterTopic; - this.EnableRawPayload = enableRawPayload; - this.OwnedMetadatas = ownedMetadatas; - this.MetadataSeparator = metadataSeparator; - } - - /// - public string Name { get; set; } - - /// - public string PubsubName { get; set; } - - /// - public bool? EnableRawPayload { get; set; } - - /// - public new string Match { get; set; } - - /// - public int Priority { get; set; } - - /// - public string[] OwnedMetadatas { get; set; } - - /// - public string MetadataSeparator { get; set; } - - /// - public string DeadLetterTopic { get; set; } + ArgumentVerifier.ThrowIfNullOrEmpty(pubsubName, nameof(pubsubName)); + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); + + this.Name = name; + this.PubsubName = pubsubName; + this.DeadLetterTopic = deadLetterTopic; + this.EnableRawPayload = enableRawPayload; + this.OwnedMetadatas = ownedMetadatas; + this.MetadataSeparator = metadataSeparator; } -} + + /// + public string Name { get; set; } + + /// + public string PubsubName { get; set; } + + /// + public bool? EnableRawPayload { get; set; } + + /// + public new string Match { get; set; } + + /// + public int Priority { get; set; } + + /// + public string[] OwnedMetadatas { get; set; } + + /// + public string MetadataSeparator { get; set; } + + /// + public string DeadLetterTopic { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/TopicMetadataAttribute.cs b/src/Dapr.AspNetCore/TopicMetadataAttribute.cs index 93d527cc4..6d2468756 100644 --- a/src/Dapr.AspNetCore/TopicMetadataAttribute.cs +++ b/src/Dapr.AspNetCore/TopicMetadataAttribute.cs @@ -11,52 +11,51 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr -{ - using System; +namespace Dapr; + +using System; +/// +/// IOriginalTopicMetadata that describes subscribe endpoint to a topic original metadata. +/// +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +public class TopicMetadataAttribute : Attribute, IOriginalTopicMetadata +{ /// - /// IOriginalTopicMetadata that describes subscribe endpoint to a topic original metadata. + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class TopicMetadataAttribute : Attribute, IOriginalTopicMetadata + /// The metadata name. + /// The metadata value. + public TopicMetadataAttribute(string name, string value) { - /// - /// Initializes a new instance of the class. - /// - /// The metadata name. - /// The metadata value. - public TopicMetadataAttribute(string name, string value) - { - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - ArgumentVerifier.ThrowIfNull(value, nameof(value)); - Name = name; - Value = value; - } + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); + ArgumentVerifier.ThrowIfNull(value, nameof(value)); + Name = name; + Value = value; + } - /// - /// Initializes a new instance of the class. - /// - /// The metadata id. - /// The metadata name. - /// The metadata value. - public TopicMetadataAttribute(string id, string name, string value) - { - ArgumentVerifier.ThrowIfNullOrEmpty(id, nameof(name)); - ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); - ArgumentVerifier.ThrowIfNull(value, nameof(value)); - Id = id; - Name = name; - Value = value; - } + /// + /// Initializes a new instance of the class. + /// + /// The metadata id. + /// The metadata name. + /// The metadata value. + public TopicMetadataAttribute(string id, string name, string value) + { + ArgumentVerifier.ThrowIfNullOrEmpty(id, nameof(name)); + ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name)); + ArgumentVerifier.ThrowIfNull(value, nameof(value)); + Id = id; + Name = name; + Value = value; + } - /// - public string Id { get; } + /// + public string Id { get; } - /// - public string Name { get; } + /// + public string Name { get; } - /// - public string Value { get; } - } -} + /// + public string Value { get; } +} \ No newline at end of file diff --git a/src/Dapr.AspNetCore/TopicOptions.cs b/src/Dapr.AspNetCore/TopicOptions.cs index 2ca2eea49..8ed031be0 100644 --- a/src/Dapr.AspNetCore/TopicOptions.cs +++ b/src/Dapr.AspNetCore/TopicOptions.cs @@ -11,57 +11,56 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +using System.Collections.Generic; +/// +/// This class defines configurations for the subscribe endpoint. +/// +public class TopicOptions { - using System.Collections.Generic; /// - /// This class defines configurations for the subscribe endpoint. + /// Gets or Sets the topic name. /// - public class TopicOptions - { - /// - /// Gets or Sets the topic name. - /// - public string Name { get; set; } + public string Name { get; set; } - /// - /// Gets or Sets the name of the pubsub component to use. - /// - public string PubsubName { get; set; } + /// + /// Gets or Sets the name of the pubsub component to use. + /// + public string PubsubName { get; set; } - /// - /// Gets or Sets a value which indicates whether to enable or disable processing raw messages. - /// - public bool EnableRawPayload { get; set; } + /// + /// Gets or Sets a value which indicates whether to enable or disable processing raw messages. + /// + public bool EnableRawPayload { get; set; } - /// - /// Gets or Sets the CEL expression to use to match events for this handler. - /// - public string Match { get; set; } + /// + /// Gets or Sets the CEL expression to use to match events for this handler. + /// + public string Match { get; set; } - /// - /// Gets or Sets the priority in which this rule should be evaluated (lower to higher). - /// - public int Priority { get; set; } + /// + /// Gets or Sets the priority in which this rule should be evaluated (lower to higher). + /// + public int Priority { get; set; } - /// - /// Gets or Sets the owned by topic. - /// - public string[] OwnedMetadatas { get; set; } + /// + /// Gets or Sets the owned by topic. + /// + public string[] OwnedMetadatas { get; set; } - /// - /// Get or Sets the separator to use for metadata. - /// - public string MetadataSeparator { get; set; } + /// + /// Get or Sets the separator to use for metadata. + /// + public string MetadataSeparator { get; set; } - /// - /// Gets or Sets the dead letter topic. - /// - public string DeadLetterTopic { get; set; } + /// + /// Gets or Sets the dead letter topic. + /// + public string DeadLetterTopic { get; set; } - /// - /// Gets or Sets the original topic metadata. - /// - public IDictionary Metadata; - } -} + /// + /// Gets or Sets the original topic metadata. + /// + public IDictionary Metadata; +} \ No newline at end of file diff --git a/src/Dapr.Client/BindingRequest.cs b/src/Dapr.Client/BindingRequest.cs index 5448588ac..46559471e 100644 --- a/src/Dapr.Client/BindingRequest.cs +++ b/src/Dapr.Client/BindingRequest.cs @@ -14,50 +14,49 @@ using System; using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Represents the request used to invoke a binding. +/// +public sealed class BindingRequest { /// - /// Represents the request used to invoke a binding. + /// Initializes a new for the provided and + /// . /// - public sealed class BindingRequest + /// The name of the binding. + /// The type of operation to perform on the binding. + public BindingRequest(string bindingName, string operation) { - /// - /// Initializes a new for the provided and - /// . - /// - /// The name of the binding. - /// The type of operation to perform on the binding. - public BindingRequest(string bindingName, string operation) - { - ArgumentVerifier.ThrowIfNullOrEmpty(bindingName, nameof(bindingName)); - ArgumentVerifier.ThrowIfNullOrEmpty(operation, nameof(operation)); - - this.BindingName = bindingName; - this.Operation = operation; - - this.Metadata = new Dictionary(); - } - - /// - /// Gets the name of the binding. - /// - /// - public string BindingName { get; } - - /// - /// Gets the type of operation to perform on the binding. - /// - public string Operation { get; } - - /// - /// Gets or sets the binding request payload. - /// - public ReadOnlyMemory Data { get; set; } - - /// - /// Gets the metadata; a collection of metadata key-value pairs that will be provided to the binding. - /// The valid metadata keys and values are determined by the type of binding used. - /// - public Dictionary Metadata { get; } + ArgumentVerifier.ThrowIfNullOrEmpty(bindingName, nameof(bindingName)); + ArgumentVerifier.ThrowIfNullOrEmpty(operation, nameof(operation)); + + this.BindingName = bindingName; + this.Operation = operation; + + this.Metadata = new Dictionary(); } -} + + /// + /// Gets the name of the binding. + /// + /// + public string BindingName { get; } + + /// + /// Gets the type of operation to perform on the binding. + /// + public string Operation { get; } + + /// + /// Gets or sets the binding request payload. + /// + public ReadOnlyMemory Data { get; set; } + + /// + /// Gets the metadata; a collection of metadata key-value pairs that will be provided to the binding. + /// The valid metadata keys and values are determined by the type of binding used. + /// + public Dictionary Metadata { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/BindingResponse.cs b/src/Dapr.Client/BindingResponse.cs index f362533e4..a134d3ac8 100644 --- a/src/Dapr.Client/BindingResponse.cs +++ b/src/Dapr.Client/BindingResponse.cs @@ -14,43 +14,42 @@ using System; using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Represents the response from invoking a binding. +/// +public sealed class BindingResponse { /// - /// Represents the response from invoking a binding. + /// Initializes a new .` /// - public sealed class BindingResponse + /// The assocated with this response. + /// The response payload. + /// The response metadata. + public BindingResponse(BindingRequest request, ReadOnlyMemory data, IReadOnlyDictionary metadata) { - /// - /// Initializes a new .` - /// - /// The assocated with this response. - /// The response payload. - /// The response metadata. - public BindingResponse(BindingRequest request, ReadOnlyMemory data, IReadOnlyDictionary metadata) - { - ArgumentVerifier.ThrowIfNull(request, nameof(request)); - ArgumentVerifier.ThrowIfNull(data, nameof(data)); - ArgumentVerifier.ThrowIfNull(metadata, nameof(metadata)); + ArgumentVerifier.ThrowIfNull(request, nameof(request)); + ArgumentVerifier.ThrowIfNull(data, nameof(data)); + ArgumentVerifier.ThrowIfNull(metadata, nameof(metadata)); - this.Request = request; - this.Data = data; - this.Metadata = metadata; - } + this.Request = request; + this.Data = data; + this.Metadata = metadata; + } - /// - /// Gets the assocated with this response. - /// - public BindingRequest Request { get; } + /// + /// Gets the assocated with this response. + /// + public BindingRequest Request { get; } - /// - /// Gets the response payload. - /// - public ReadOnlyMemory Data { get; } + /// + /// Gets the response payload. + /// + public ReadOnlyMemory Data { get; } - /// - /// Gets the response metadata. - /// - public IReadOnlyDictionary Metadata { get; } - } -} + /// + /// Gets the response metadata. + /// + public IReadOnlyDictionary Metadata { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/BulkPublishEntry.cs b/src/Dapr.Client/BulkPublishEntry.cs index d6b9a5497..8f9757aef 100644 --- a/src/Dapr.Client/BulkPublishEntry.cs +++ b/src/Dapr.Client/BulkPublishEntry.cs @@ -13,49 +13,48 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Class representing an entry in the BulkPublishRequest. +/// +/// The data type of the value. +public class BulkPublishEntry { /// - /// Class representing an entry in the BulkPublishRequest. + /// Initializes a new instance of the class. /// - /// The data type of the value. - public class BulkPublishEntry + /// A request scoped ID uniquely identifying this entry in the BulkPublishRequest. + /// Event to be published. + /// Content Type of the event to be published. + /// Metadata for the event. + public BulkPublishEntry(string entryId, TValue eventData, string contentType, IReadOnlyDictionary metadata = default) { - /// - /// Initializes a new instance of the class. - /// - /// A request scoped ID uniquely identifying this entry in the BulkPublishRequest. - /// Event to be published. - /// Content Type of the event to be published. - /// Metadata for the event. - public BulkPublishEntry(string entryId, TValue eventData, string contentType, IReadOnlyDictionary metadata = default) - { - this.EntryId = entryId; - this.EventData = eventData; - this.ContentType = contentType; - this.Metadata = metadata; - } + this.EntryId = entryId; + this.EventData = eventData; + this.ContentType = contentType; + this.Metadata = metadata; + } - /// - /// The ID uniquely identifying this particular request entry across the request and scoped for this request only. - /// - public string EntryId { get; } + /// + /// The ID uniquely identifying this particular request entry across the request and scoped for this request only. + /// + public string EntryId { get; } - /// - /// The event to be published. - /// - public TValue EventData { get; } + /// + /// The event to be published. + /// + public TValue EventData { get; } - /// - /// The content type of the event to be published. - /// - public string ContentType { get; } + /// + /// The content type of the event to be published. + /// + public string ContentType { get; } - /// - /// The metadata set for this particular event. - /// Any particular values in this metadata overrides the request metadata present in BulkPublishRequest. - /// - public IReadOnlyDictionary Metadata { get; } + /// + /// The metadata set for this particular event. + /// Any particular values in this metadata overrides the request metadata present in BulkPublishRequest. + /// + public IReadOnlyDictionary Metadata { get; } - } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/BulkPublishResponse.cs b/src/Dapr.Client/BulkPublishResponse.cs index f37c35aa0..602d3a334 100644 --- a/src/Dapr.Client/BulkPublishResponse.cs +++ b/src/Dapr.Client/BulkPublishResponse.cs @@ -13,25 +13,24 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Class representing the response returned on bulk publishing events. +/// +public class BulkPublishResponse { /// - /// Class representing the response returned on bulk publishing events. + /// Initializes a new instance of the class. /// - public class BulkPublishResponse + /// The List of BulkPublishResponseEntries representing the list of events that failed to be published. + public BulkPublishResponse(List> failedEntries) { - /// - /// Initializes a new instance of the class. - /// - /// The List of BulkPublishResponseEntries representing the list of events that failed to be published. - public BulkPublishResponse(List> failedEntries) - { - this.FailedEntries = failedEntries; - } - - /// - /// The List of BulkPublishResponseFailedEntry objects that have failed to publish. - /// - public List> FailedEntries { get; } + this.FailedEntries = failedEntries; } -} + + /// + /// The List of BulkPublishResponseFailedEntry objects that have failed to publish. + /// + public List> FailedEntries { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/BulkPublishResponseFailedEntry.cs b/src/Dapr.Client/BulkPublishResponseFailedEntry.cs index e8a46c9bb..7357647ce 100644 --- a/src/Dapr.Client/BulkPublishResponseFailedEntry.cs +++ b/src/Dapr.Client/BulkPublishResponseFailedEntry.cs @@ -11,32 +11,31 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Class representing the status of each event that was published using BulkPublishRequest. +/// +public class BulkPublishResponseFailedEntry { /// - /// Class representing the status of each event that was published using BulkPublishRequest. + /// Initializes a new instance of the class. /// - public class BulkPublishResponseFailedEntry + /// The entry that failed to be published. + /// Error message as to why the entry failed to publish. + public BulkPublishResponseFailedEntry(BulkPublishEntry entry, string errorMessage) { - /// - /// Initializes a new instance of the class. - /// - /// The entry that failed to be published. - /// Error message as to why the entry failed to publish. - public BulkPublishResponseFailedEntry(BulkPublishEntry entry, string errorMessage) - { - this.Entry = entry; - this.ErrorMessage = errorMessage; - } + this.Entry = entry; + this.ErrorMessage = errorMessage; + } - /// - /// The entry that has failed. - /// - public BulkPublishEntry Entry { get; } + /// + /// The entry that has failed. + /// + public BulkPublishEntry Entry { get; } - /// - /// Error message as to why the entry failed to publish. - /// - public string ErrorMessage { get; } - } -} + /// + /// Error message as to why the entry failed to publish. + /// + public string ErrorMessage { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/BulkStateItem.cs b/src/Dapr.Client/BulkStateItem.cs index 5b30ddf21..f834654db 100644 --- a/src/Dapr.Client/BulkStateItem.cs +++ b/src/Dapr.Client/BulkStateItem.cs @@ -11,80 +11,79 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Represents a state object returned from a bulk get state operation. +/// +public readonly struct BulkStateItem { /// - /// Represents a state object returned from a bulk get state operation. + /// Initializes a new instance of the class. /// - public readonly struct BulkStateItem + /// The state key. + /// The value. + /// The ETag. + /// + /// Application code should not need to create instances of . + /// + public BulkStateItem(string key, string value, string etag) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The value. - /// The ETag. - /// - /// Application code should not need to create instances of . - /// - public BulkStateItem(string key, string value, string etag) - { - this.Key = key; - this.Value = value; - this.ETag = etag; - } + this.Key = key; + this.Value = value; + this.ETag = etag; + } - /// - /// Gets the state key. - /// - public string Key { get; } + /// + /// Gets the state key. + /// + public string Key { get; } - /// - /// Gets the value. - /// - public string Value { get; } + /// + /// Gets the value. + /// + public string Value { get; } - /// - /// Get the ETag. - /// - public string ETag { get; } - } + /// + /// Get the ETag. + /// + public string ETag { get; } +} +/// +/// Represents a state object returned from a bulk get state operation where the value has +/// been deserialized to the specified type. +/// +public readonly struct BulkStateItem +{ /// - /// Represents a state object returned from a bulk get state operation where the value has - /// been deserialized to the specified type. + /// Initializes a new instance of the class. /// - public readonly struct BulkStateItem + /// The state key. + /// The typed value. + /// The ETag. + /// + /// Application code should not need to create instances of . + /// + public BulkStateItem(string key, TValue value, string etag) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The typed value. - /// The ETag. - /// - /// Application code should not need to create instances of . - /// - public BulkStateItem(string key, TValue value, string etag) - { - this.Key = key; - this.Value = value; - this.ETag = etag; - } + this.Key = key; + this.Value = value; + this.ETag = etag; + } - /// - /// Gets the state key. - /// - public string Key { get; } + /// + /// Gets the state key. + /// + public string Key { get; } - /// - /// Gets the deserialized value of the indicated type. - /// - public TValue Value { get; } + /// + /// Gets the deserialized value of the indicated type. + /// + public TValue Value { get; } - /// - /// Get the ETag. - /// - public string ETag { get; } - } -} + /// + /// Get the ETag. + /// + public string ETag { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/CloudEvent.cs b/src/Dapr.Client/CloudEvent.cs index bb0eaf464..f9329cd21 100644 --- a/src/Dapr.Client/CloudEvent.cs +++ b/src/Dapr.Client/CloudEvent.cs @@ -15,54 +15,53 @@ using System.Text.Json.Serialization; using Dapr.Client; -namespace Dapr +namespace Dapr; + +/// +/// Represents a CloudEvent without data. +/// +public class CloudEvent { - /// - /// Represents a CloudEvent without data. - /// - public class CloudEvent - { - /// - /// CloudEvent 'source' attribute. - /// - [JsonPropertyName("source")] - public Uri Source { get; init; } + /// + /// CloudEvent 'source' attribute. + /// + [JsonPropertyName("source")] + public Uri Source { get; init; } - /// - /// CloudEvent 'type' attribute. - /// - [JsonPropertyName("type")] - public string Type { get; init; } + /// + /// CloudEvent 'type' attribute. + /// + [JsonPropertyName("type")] + public string Type { get; init; } - /// - /// CloudEvent 'subject' attribute. - /// - [JsonPropertyName("subject")] - public string Subject { get; init; } - } + /// + /// CloudEvent 'subject' attribute. + /// + [JsonPropertyName("subject")] + public string Subject { get; init; } +} - /// - /// Represents a CloudEvent with typed data. - /// - public class CloudEvent : CloudEvent - { - /// - /// Initialize a new instance of the class. - /// - public CloudEvent(TData data) - { - Data = data; - } +/// +/// Represents a CloudEvent with typed data. +/// +public class CloudEvent : CloudEvent +{ + /// + /// Initialize a new instance of the class. + /// + public CloudEvent(TData data) + { + Data = data; + } - /// - /// CloudEvent 'data' content. - /// - public TData Data { get; } + /// + /// CloudEvent 'data' content. + /// + public TData Data { get; } - /// - /// Gets event data. - /// - [JsonPropertyName("datacontenttype")] - public string DataContentType { get; } = Constants.ContentTypeApplicationJson; - } -} + /// + /// Gets event data. + /// + [JsonPropertyName("datacontenttype")] + public string DataContentType { get; } = Constants.ContentTypeApplicationJson; +} \ No newline at end of file diff --git a/src/Dapr.Client/ConcurrencyMode.cs b/src/Dapr.Client/ConcurrencyMode.cs index 4efc48d80..4f2a2e8f9 100644 --- a/src/Dapr.Client/ConcurrencyMode.cs +++ b/src/Dapr.Client/ConcurrencyMode.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Concurrency mode for state operations with Dapr. +/// +public enum ConcurrencyMode { /// - /// Concurrency mode for state operations with Dapr. + /// State operations will be handled in a first-write-wins fashion /// - public enum ConcurrencyMode - { - /// - /// State operations will be handled in a first-write-wins fashion - /// - FirstWrite, + FirstWrite, - /// - /// State operations will be handled in a last-write-wins fashion - /// - LastWrite, - } -} + /// + /// State operations will be handled in a last-write-wins fashion + /// + LastWrite, +} \ No newline at end of file diff --git a/src/Dapr.Client/ConfigurationItem.cs b/src/Dapr.Client/ConfigurationItem.cs index fde406dbb..fb4cfef62 100644 --- a/src/Dapr.Client/ConfigurationItem.cs +++ b/src/Dapr.Client/ConfigurationItem.cs @@ -1,39 +1,38 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Class that represents an item fetched from the Dapr Configuration API. +/// +public class ConfigurationItem { /// - /// Class that represents an item fetched from the Dapr Configuration API. + /// Constructor for a ConfigurationItem. /// - public class ConfigurationItem + /// The value of the configuration item. + /// The version of the fetched item. + /// The metadata associated with the request. + public ConfigurationItem(string value, string version, IReadOnlyDictionary metadata) { - /// - /// Constructor for a ConfigurationItem. - /// - /// The value of the configuration item. - /// The version of the fetched item. - /// The metadata associated with the request. - public ConfigurationItem(string value, string version, IReadOnlyDictionary metadata) - { - Value = value; - Version = version; - Metadata = metadata; - } + Value = value; + Version = version; + Metadata = metadata; + } - /// - /// The value of the configuration item. - /// - public string Value { get; } + /// + /// The value of the configuration item. + /// + public string Value { get; } - /// - /// The version of the item retrieved. This is only provided on responses and - /// the statestore is not expected to keep all versions available. - /// - public string Version { get; } + /// + /// The version of the item retrieved. This is only provided on responses and + /// the statestore is not expected to keep all versions available. + /// + public string Version { get; } - /// - /// The metadata that is passed to/from the statestore component. - /// - public IReadOnlyDictionary Metadata { get; } - } -} + /// + /// The metadata that is passed to/from the statestore component. + /// + public IReadOnlyDictionary Metadata { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/ConfigurationSource.cs b/src/Dapr.Client/ConfigurationSource.cs index ca3f8d6d2..2c8ee31bd 100644 --- a/src/Dapr.Client/ConfigurationSource.cs +++ b/src/Dapr.Client/ConfigurationSource.cs @@ -14,19 +14,18 @@ using System.Collections.Generic; using System.Threading; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Abstraction around a configuration source. +/// +public abstract class ConfigurationSource : IAsyncEnumerable> { /// - /// Abstraction around a configuration source. + /// The Id associated with this configuration source. /// - public abstract class ConfigurationSource : IAsyncEnumerable> - { - /// - /// The Id associated with this configuration source. - /// - public abstract string Id { get; } + public abstract string Id { get; } - /// - public abstract IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default); - } -} + /// + public abstract IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/Dapr.Client/ConsistencyMode.cs b/src/Dapr.Client/ConsistencyMode.cs index 91f13ca5e..5492d5152 100644 --- a/src/Dapr.Client/ConsistencyMode.cs +++ b/src/Dapr.Client/ConsistencyMode.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Consistency mode for state operations with Dapr. +/// +public enum ConsistencyMode { /// - /// Consistency mode for state operations with Dapr. + /// Eventual consistency. /// - public enum ConsistencyMode - { - /// - /// Eventual consistency. - /// - Eventual, + Eventual, - /// - /// Strong consistency. - /// - Strong, - } -} + /// + /// Strong consistency. + /// + Strong, +} \ No newline at end of file diff --git a/src/Dapr.Client/Constants.cs b/src/Dapr.Client/Constants.cs index 37ebbb21b..206fe1c2a 100644 --- a/src/Dapr.Client/Constants.cs +++ b/src/Dapr.Client/Constants.cs @@ -13,12 +13,11 @@ using System.Net.Mime; -namespace Dapr.Client +namespace Dapr.Client; + +internal class Constants { - internal class Constants - { - public const string ContentTypeApplicationJson = MediaTypeNames.Application.Json; - public const string ContentTypeApplicationGrpc = "application/grpc"; - public const string ContentTypeCloudEvent = "application/cloudevents+json"; - } -} + public const string ContentTypeApplicationJson = MediaTypeNames.Application.Json; + public const string ContentTypeApplicationGrpc = "application/grpc"; + public const string ContentTypeCloudEvent = "application/cloudevents+json"; +} \ No newline at end of file diff --git a/src/Dapr.Client/CryptographyEnums.cs b/src/Dapr.Client/CryptographyEnums.cs index f5955b389..949de7deb 100644 --- a/src/Dapr.Client/CryptographyEnums.cs +++ b/src/Dapr.Client/CryptographyEnums.cs @@ -13,64 +13,63 @@ using System.Runtime.Serialization; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// The cipher used for data encryption operations. +/// +public enum DataEncryptionCipher { /// - /// The cipher used for data encryption operations. + /// The default data encryption cipher used, this represents AES GCM. + /// + [EnumMember(Value = "aes-gcm")] + AesGcm, + /// + /// Represents the ChaCha20-Poly1305 data encryption cipher. /// - public enum DataEncryptionCipher - { - /// - /// The default data encryption cipher used, this represents AES GCM. - /// - [EnumMember(Value = "aes-gcm")] - AesGcm, - /// - /// Represents the ChaCha20-Poly1305 data encryption cipher. - /// - [EnumMember(Value = "chacha20-poly1305")] - ChaCha20Poly1305 - }; + [EnumMember(Value = "chacha20-poly1305")] + ChaCha20Poly1305 +}; +/// +/// The algorithm used for key wrapping cryptographic operations. +/// +public enum KeyWrapAlgorithm +{ + /// + /// Represents the AES key wrap algorithm. + /// + [EnumMember(Value="A256KW")] + Aes, + /// + /// An alias for the AES key wrap algorithm. + /// + [EnumMember(Value="A256KW")] + A256kw, + /// + /// Represents the AES 128 CBC key wrap algorithm. + /// + [EnumMember(Value="A128CBC")] + A128cbc, + /// + /// Represents the AES 192 CBC key wrap algorithm. + /// + [EnumMember(Value="A192CBC")] + A192cbc, + /// + /// Represents the AES 256 CBC key wrap algorithm. + /// + [EnumMember(Value="A256CBC")] + A256cbc, + /// + /// Represents the RSA key wrap algorithm. + /// + [EnumMember(Value= "RSA-OAEP-256")] + Rsa, /// - /// The algorithm used for key wrapping cryptographic operations. + /// An alias for the RSA key wrap algorithm. /// - public enum KeyWrapAlgorithm - { - /// - /// Represents the AES key wrap algorithm. - /// - [EnumMember(Value="A256KW")] - Aes, - /// - /// An alias for the AES key wrap algorithm. - /// - [EnumMember(Value="A256KW")] - A256kw, - /// - /// Represents the AES 128 CBC key wrap algorithm. - /// - [EnumMember(Value="A128CBC")] - A128cbc, - /// - /// Represents the AES 192 CBC key wrap algorithm. - /// - [EnumMember(Value="A192CBC")] - A192cbc, - /// - /// Represents the AES 256 CBC key wrap algorithm. - /// - [EnumMember(Value="A256CBC")] - A256cbc, - /// - /// Represents the RSA key wrap algorithm. - /// - [EnumMember(Value= "RSA-OAEP-256")] - Rsa, - /// - /// An alias for the RSA key wrap algorithm. - /// - [EnumMember(Value= "RSA-OAEP-256")] - RsaOaep256 //Alias for RSA - } -} + [EnumMember(Value= "RSA-OAEP-256")] + RsaOaep256 //Alias for RSA +} \ No newline at end of file diff --git a/src/Dapr.Client/CryptographyOptions.cs b/src/Dapr.Client/CryptographyOptions.cs index ae94a8f2f..227753991 100644 --- a/src/Dapr.Client/CryptographyOptions.cs +++ b/src/Dapr.Client/CryptographyOptions.cs @@ -1,80 +1,79 @@ #nullable enable using System; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// A collection of options used to configure how encryption cryptographic operations are performed. +/// +public class EncryptionOptions { /// - /// A collection of options used to configure how encryption cryptographic operations are performed. + /// Creates a new instance of the . /// - public class EncryptionOptions + /// + public EncryptionOptions(KeyWrapAlgorithm keyWrapAlgorithm) { - /// - /// Creates a new instance of the . - /// - /// - public EncryptionOptions(KeyWrapAlgorithm keyWrapAlgorithm) - { - KeyWrapAlgorithm = keyWrapAlgorithm; - } + KeyWrapAlgorithm = keyWrapAlgorithm; + } - /// - /// The name of the algorithm used to wrap the encryption key. - /// - public KeyWrapAlgorithm KeyWrapAlgorithm { get; set; } + /// + /// The name of the algorithm used to wrap the encryption key. + /// + public KeyWrapAlgorithm KeyWrapAlgorithm { get; set; } - private int streamingBlockSizeInBytes = 4 * 1024; // 4 KB - /// - /// The size of the block in bytes used to send data to the sidecar for cryptography operations. - /// - /// - /// This defaults to 4KB and generally should not exceed 64KB. - /// - public int StreamingBlockSizeInBytes + private int streamingBlockSizeInBytes = 4 * 1024; // 4 KB + /// + /// The size of the block in bytes used to send data to the sidecar for cryptography operations. + /// + /// + /// This defaults to 4KB and generally should not exceed 64KB. + /// + public int StreamingBlockSizeInBytes + { + get => streamingBlockSizeInBytes; + set { - get => streamingBlockSizeInBytes; - set + if (value <= 0) { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - streamingBlockSizeInBytes = value; + throw new ArgumentOutOfRangeException(nameof(value)); } + + streamingBlockSizeInBytes = value; } + } - /// - /// The optional name (and optionally a version) of the key specified to use during decryption. - /// - public string? DecryptionKeyName { get; set; } = null; + /// + /// The optional name (and optionally a version) of the key specified to use during decryption. + /// + public string? DecryptionKeyName { get; set; } = null; - /// - /// The name of the cipher to use for the encryption operation. - /// - public DataEncryptionCipher EncryptionCipher { get; set; } = DataEncryptionCipher.AesGcm; - } + /// + /// The name of the cipher to use for the encryption operation. + /// + public DataEncryptionCipher EncryptionCipher { get; set; } = DataEncryptionCipher.AesGcm; +} +/// +/// A collection fo options used to configure how decryption cryptographic operations are performed. +/// +public class DecryptionOptions +{ + private int streamingBlockSizeInBytes = 4 * 1024; // 4KB /// - /// A collection fo options used to configure how decryption cryptographic operations are performed. + /// The size of the block in bytes used to send data to the sidecar for cryptography operations. /// - public class DecryptionOptions + public int StreamingBlockSizeInBytes { - private int streamingBlockSizeInBytes = 4 * 1024; // 4KB - /// - /// The size of the block in bytes used to send data to the sidecar for cryptography operations. - /// - public int StreamingBlockSizeInBytes + get => streamingBlockSizeInBytes; + set { - get => streamingBlockSizeInBytes; - set + if (value <= 0) { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - streamingBlockSizeInBytes = value; + throw new ArgumentOutOfRangeException(nameof(value)); } + + streamingBlockSizeInBytes = value; } } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/DaprApiException.cs b/src/Dapr.Client/DaprApiException.cs index 75fc2cf7f..adb3021a7 100644 --- a/src/Dapr.Client/DaprApiException.cs +++ b/src/Dapr.Client/DaprApiException.cs @@ -11,125 +11,124 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr -{ - using System; - using System.Runtime.Serialization; +namespace Dapr; +using System; +using System.Runtime.Serialization; + +/// +/// Exception for Dapr operations. +/// +[Serializable] +public class DaprApiException : DaprException +{ /// - /// Exception for Dapr operations. + /// Initializes a new instance of the class with error code 'UNKNOWN'"/>. /// - [Serializable] - public class DaprApiException : DaprException - { - /// - /// Initializes a new instance of the class with error code 'UNKNOWN'"/>. - /// - public DaprApiException() - : this(errorCode: null, false) - { - } + public DaprApiException() + : this(errorCode: null, false) + { + } - /// - /// Initializes a new instance of the class with error code 'UNKNOWN' and a specified error message. - /// - /// The error message that explains the reason for the exception. - public DaprApiException(string message) - : this(message, errorCode: null, false) - { - } + /// + /// Initializes a new instance of the class with error code 'UNKNOWN' and a specified error message. + /// + /// The error message that explains the reason for the exception. + public DaprApiException(string message) + : this(message, errorCode: null, false) + { + } - /// - /// Initializes a new instance of the class with a specified error - /// message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - public DaprApiException(string message, Exception innerException) - : this(message, innerException, errorCode: null, false) - { - } + /// + /// Initializes a new instance of the class with a specified error + /// message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public DaprApiException(string message, Exception innerException) + : this(message, innerException, errorCode: null, false) + { + } - /// - /// Initializes a new instance of the class with a specified error code. - /// - /// The error code associated with the exception. - /// True, if the exception is to be treated as an transient exception. - public DaprApiException(string errorCode, bool isTransient) - : this(string.Empty, errorCode, isTransient) - { - } + /// + /// Initializes a new instance of the class with a specified error code. + /// + /// The error code associated with the exception. + /// True, if the exception is to be treated as an transient exception. + public DaprApiException(string errorCode, bool isTransient) + : this(string.Empty, errorCode, isTransient) + { + } - /// - /// Initializes a new instance of the class with specified error message and error code. - /// - /// The error message that explains the reason for the exception. - /// The error code associated with the exception. - /// Indicating if its an transient exception. - public DaprApiException(string message, string errorCode, bool isTransient) - : this(message, null, errorCode, isTransient) - { - } + /// + /// Initializes a new instance of the class with specified error message and error code. + /// + /// The error message that explains the reason for the exception. + /// The error code associated with the exception. + /// Indicating if its an transient exception. + public DaprApiException(string message, string errorCode, bool isTransient) + : this(message, null, errorCode, isTransient) + { + } - /// - /// Initializes a new instance of the class. - /// Initializes a new instance of class - /// with a specified error message, a reference to the inner exception that is the cause of this exception, and a specified error code. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception or null if no inner exception is specified. The class provides more details about the inner exception.. - /// The error code associated with the exception. - /// Indicating if its an transient exception. - public DaprApiException(string message, Exception inner, string errorCode, bool isTransient) - : base(message, inner) - { - this.ErrorCode = errorCode ?? "UNKNOWN"; - this.IsTransient = isTransient; - } + /// + /// Initializes a new instance of the class. + /// Initializes a new instance of class + /// with a specified error message, a reference to the inner exception that is the cause of this exception, and a specified error code. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception or null if no inner exception is specified. The class provides more details about the inner exception.. + /// The error code associated with the exception. + /// Indicating if its an transient exception. + public DaprApiException(string message, Exception inner, string errorCode, bool isTransient) + : base(message, inner) + { + this.ErrorCode = errorCode ?? "UNKNOWN"; + this.IsTransient = isTransient; + } - /// - /// Initializes a new instance of the class with a specified context. - /// - /// The object that contains serialized object data of the exception being thrown. - /// The object that contains contextual information about the source or destination. The context parameter is reserved for future use and can be null. + /// + /// Initializes a new instance of the class with a specified context. + /// + /// The object that contains serialized object data of the exception being thrown. + /// The object that contains contextual information about the source or destination. The context parameter is reserved for future use and can be null. #if NET8_0_OR_GREATER [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to the serialization ctor #endif - protected DaprApiException(SerializationInfo info, StreamingContext context) - : base(info, context) + protected DaprApiException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info != null) { - if (info != null) - { - this.ErrorCode = (string)info.GetValue(nameof(this.ErrorCode), typeof(string)); - this.IsTransient = info.GetBoolean(nameof(this.IsTransient)); - } + this.ErrorCode = (string)info.GetValue(nameof(this.ErrorCode), typeof(string)); + this.IsTransient = info.GetBoolean(nameof(this.IsTransient)); } + } - /// - /// Gets the error code parameter. - /// - /// The error code associated with the exception. - public string ErrorCode { get; } + /// + /// Gets the error code parameter. + /// + /// The error code associated with the exception. + public string ErrorCode { get; } - /// - /// Gets a value indicating whether gets exception is Transient and operation can be retried. - /// - /// Value indicating whether the exception is transient or not. - public bool IsTransient { get; } = false; + /// + /// Gets a value indicating whether gets exception is Transient and operation can be retried. + /// + /// Value indicating whether the exception is transient or not. + public bool IsTransient { get; } = false; - /// + /// #if NET8_0_OR_GREATER [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to GetObjectData #endif - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); - if (info != null) - { - info.AddValue(nameof(this.ErrorCode), this.ErrorCode); - info.AddValue(nameof(this.IsTransient), this.IsTransient); - } + if (info != null) + { + info.AddValue(nameof(this.ErrorCode), this.ErrorCode); + info.AddValue(nameof(this.IsTransient), this.IsTransient); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 6be31a648..98e96f595 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -26,1403 +26,1402 @@ using Grpc.Core.Interceptors; using Grpc.Net.Client; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// +/// Defines client methods for interacting with Dapr endpoints. +/// Use to create . +/// +/// +/// Implementations of implement because the client +/// accesses network resources. For best performance, create a single long-lived client instance and share +/// it for the lifetime of the application. Avoid creating and disposing a client instance for each operation +/// that the application performs - this can lead to socket exhaustion and other problems. +/// +/// +public abstract class DaprClient : IDisposable { + private bool disposed; + + /// + /// Gets the used for JSON serialization operations. + /// + public abstract JsonSerializerOptions JsonSerializerOptions { get; } + /// /// - /// Defines client methods for interacting with Dapr endpoints. - /// Use to create . + /// Creates an that can be used to perform Dapr service + /// invocation using objects. /// /// - /// Implementations of implement because the client - /// accesses network resources. For best performance, create a single long-lived client instance and share - /// it for the lifetime of the application. Avoid creating and disposing a client instance for each operation - /// that the application performs - this can lead to socket exhaustion and other problems. + /// 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 rewritten to follow the format of a Dapr service invocation request. /// /// - public abstract class DaprClient : IDisposable + /// + /// 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. + /// + /// The HTTP endpoint of the Dapr process to use for service invocation calls. + /// The token to be added to all request headers to Dapr runtime. + /// An that can be used to perform service invocation requests. + /// + /// + /// The object is intended to be a long-lived and holds access to networking resources. + /// Since the value of will not change during the lifespan of the application, + /// a single client object can be reused for the life of the application. + /// + /// + public static HttpClient CreateInvokeHttpClient(string appId = null, string daprEndpoint = null, string daprApiToken = null) { - private bool disposed; - - /// - /// Gets the used for JSON serialization operations. - /// - public abstract JsonSerializerOptions JsonSerializerOptions { get; } - - /// - /// - /// 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 - /// 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. - /// - /// The HTTP endpoint of the Dapr process to use for service invocation calls. - /// The token to be added to all request headers to Dapr runtime. - /// An that can be used to perform service invocation requests. - /// - /// - /// The object is intended to be a long-lived and holds access to networking resources. - /// Since the value of will not change during the lifespan of the application, - /// a single client object can be reused for the life of the application. - /// - /// - public static HttpClient CreateInvokeHttpClient(string appId = null, string daprEndpoint = null, string daprApiToken = null) + var handler = new InvocationHandler() { - var handler = new InvocationHandler() - { - InnerHandler = new HttpClientHandler(), - DaprApiToken = daprApiToken, - DefaultAppId = appId, - }; + InnerHandler = new HttpClientHandler(), + DaprApiToken = daprApiToken, + DefaultAppId = appId, + }; - if (daprEndpoint is string) - { - // DaprEndpoint performs validation. - handler.DaprEndpoint = daprEndpoint; - } + if (daprEndpoint is string) + { + // DaprEndpoint performs validation. + handler.DaprEndpoint = daprEndpoint; + } - var httpClient = new HttpClient(handler); - httpClient.DefaultRequestHeaders.UserAgent.Add(UserAgent()); + var httpClient = new HttpClient(handler); + httpClient.DefaultRequestHeaders.UserAgent.Add(UserAgent()); - if (appId is string) + if (appId is string) + { + try { - try - { - httpClient.BaseAddress = new Uri($"http://{appId}"); - } - catch (UriFormatException inner) - { - throw new ArgumentException("The appId must be a valid hostname.", nameof(appId), inner); - } + httpClient.BaseAddress = new Uri($"http://{appId}"); + } + catch (UriFormatException inner) + { + throw new ArgumentException("The appId must be a valid hostname.", nameof(appId), inner); } - - return httpClient; } - /// - /// - /// Creates an that can be used to perform locally defined gRPC calls - /// using the Dapr sidecar as a proxy. - /// - /// - /// The created is used to intercept a with an - /// . The interceptor inserts the and, if present, - /// the into the request's metadata. - /// - /// - /// - /// The appId that is targetted by Dapr for gRPC invocations. - /// - /// - /// Optional gRPC endpoint for calling Dapr, defaults to . - /// - /// - /// Optional token to be attached to all requests, defaults to . - /// - /// An to be used for proxied gRPC calls through Dapr. - /// - /// - /// As the will remain constant, a single instance of the - /// can be used throughout the lifetime of the application. - /// - /// - public static CallInvoker CreateInvocationInvoker(string appId, string daprEndpoint = null, string daprApiToken = null) - { - var channel = GrpcChannel.ForAddress(daprEndpoint ?? DaprDefaults.GetDefaultGrpcEndpoint()); - return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null))); - } + return httpClient; + } - internal static KeyValuePair? GetDaprApiTokenHeader(string apiToken) - { - return string.IsNullOrWhiteSpace(apiToken) - ? null - : new KeyValuePair("dapr-api-token", apiToken); - } + /// + /// + /// Creates an that can be used to perform locally defined gRPC calls + /// using the Dapr sidecar as a proxy. + /// + /// + /// The created is used to intercept a with an + /// . The interceptor inserts the and, if present, + /// the into the request's metadata. + /// + /// + /// + /// The appId that is targetted by Dapr for gRPC invocations. + /// + /// + /// Optional gRPC endpoint for calling Dapr, defaults to . + /// + /// + /// Optional token to be attached to all requests, defaults to . + /// + /// An to be used for proxied gRPC calls through Dapr. + /// + /// + /// As the will remain constant, a single instance of the + /// can be used throughout the lifetime of the application. + /// + /// + public static CallInvoker CreateInvocationInvoker(string appId, string daprEndpoint = null, string daprApiToken = null) + { + var channel = GrpcChannel.ForAddress(daprEndpoint ?? DaprDefaults.GetDefaultGrpcEndpoint()); + return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null))); + } - /// - /// Publishes an event to the specified topic. - /// - /// The name of the pubsub component to use. - /// The name of the topic the request should be published to. - /// The data that will be JSON serialized and provided as the event payload. - /// A that can be used to cancel the operation. - /// The type of the data that will be JSON serialized and provided as the event payload. - /// A that will complete when the operation has completed. - public abstract Task PublishEventAsync( - string pubsubName, - string topicName, - TData data, - CancellationToken cancellationToken = default); - - /// - /// Publishes an event to the specified topic. - /// - /// The name of the pubsub component to use. - /// The name of the topic the request should be published to. - /// The data that will be JSON serialized and provided as the event payload. - /// - /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values - /// are determined by the type of pubsub component used. - /// - /// A that can be used to cancel the operation. - /// The type of the data that will be JSON serialized and provided as the event payload. - /// A that will complete when the operation has completed. - public abstract Task PublishEventAsync( - string pubsubName, - string topicName, - TData data, - Dictionary metadata, - CancellationToken cancellationToken = default); - - /// - /// Publishes an event to the specified topic. - /// - /// The name of the pubsub component to use. - /// The name of the topic the request should be published to. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task PublishEventAsync( - string pubsubName, - string topicName, - CancellationToken cancellationToken = default); - - /// - /// Publishes an event to the specified topic. - /// - /// The name of the pubsub component to use. - /// The name of the topic the request should be published to. - /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values are determined by the type of binding used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task PublishEventAsync( - string pubsubName, - string topicName, - Dictionary metadata, - CancellationToken cancellationToken = default); - - /// - /// // Bulk Publishes multiple events to the specified topic. - /// - /// The name of the pubsub component to use. - /// The name of the topic the request should be published to. - /// The list of events to be published. - /// The metadata to be set at the request level for the request. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task> BulkPublishEventAsync( - string pubsubName, - string topicName, - IReadOnlyList events, - Dictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Publishes an event to the specified topic. - /// - /// The name of the pubsub component to use. - /// The name of the topic the request should be published to. - /// The raw byte payload to inlcude in the message. - /// The content type of the given bytes, defaults to application/json. - /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values are determined by the type of binding used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task PublishByteEventAsync( - string pubsubName, - string topicName, - ReadOnlyMemory data, - string dataContentType = Constants.ContentTypeApplicationJson, - Dictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Invokes an output binding. - /// - /// The type of the data that will be JSON serialized and provided as the binding payload. - /// The name of the binding to sent the event to. - /// The type of operation to perform on the binding. - /// The data that will be JSON serialized and provided as the binding payload. - /// A collection of metadata key-value pairs that will be provided to the binding. The valid metadata keys and values are determined by the type of binding used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task InvokeBindingAsync( - string bindingName, - string operation, - TRequest data, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Invokes an output binding. - /// - /// The type of the data that will be JSON serialized and provided as the binding payload. - /// The type of the data that will be JSON deserialized from the binding response. - /// The name of the binding to sent the event to. - /// The type of operation to perform on the binding. - /// The data that will be JSON serialized and provided as the binding payload. - /// A collection of metadata key-value pairs that will be provided to the binding. The valid metadata keys and values are determined by the type of binding used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task InvokeBindingAsync( - string bindingName, - string operation, - TRequest data, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Invokes a binding with the provided . This method allows for control of the binding - /// input and output using raw bytes. - /// - /// The to send. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task InvokeBindingAsync( - BindingRequest request, - CancellationToken cancellationToken = default); - - /// - /// Creates an that can be used to perform service invocation for the - /// application identified by and invokes the method specified by - /// with the POST HTTP method. - /// - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// An for use with SendInvokeMethodRequestAsync. - public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName) - { - return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName); - } + internal static KeyValuePair? GetDaprApiTokenHeader(string apiToken) + { + return string.IsNullOrWhiteSpace(apiToken) + ? null + : new KeyValuePair("dapr-api-token", apiToken); + } - /// - /// Creates an that can be used to perform service invocation for the - /// application identified by and invokes the method specified by - /// with the POST HTTP method. - /// - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A collection of key/value pairs to populate the query string from. - /// An for use with SendInvokeMethodRequestAsync. - public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, IReadOnlyCollection> queryStringParameters) - { - return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, queryStringParameters); - } + /// + /// Publishes an event to the specified topic. + /// + /// The name of the pubsub component to use. + /// The name of the topic the request should be published to. + /// The data that will be JSON serialized and provided as the event payload. + /// A that can be used to cancel the operation. + /// The type of the data that will be JSON serialized and provided as the event payload. + /// A that will complete when the operation has completed. + public abstract Task PublishEventAsync( + string pubsubName, + string topicName, + TData data, + CancellationToken cancellationToken = default); + + /// + /// Publishes an event to the specified topic. + /// + /// The name of the pubsub component to use. + /// The name of the topic the request should be published to. + /// The data that will be JSON serialized and provided as the event payload. + /// + /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values + /// are determined by the type of pubsub component used. + /// + /// A that can be used to cancel the operation. + /// The type of the data that will be JSON serialized and provided as the event payload. + /// A that will complete when the operation has completed. + public abstract Task PublishEventAsync( + string pubsubName, + string topicName, + TData data, + Dictionary metadata, + CancellationToken cancellationToken = default); + + /// + /// Publishes an event to the specified topic. + /// + /// The name of the pubsub component to use. + /// The name of the topic the request should be published to. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task PublishEventAsync( + string pubsubName, + string topicName, + CancellationToken cancellationToken = default); + + /// + /// Publishes an event to the specified topic. + /// + /// The name of the pubsub component to use. + /// The name of the topic the request should be published to. + /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values are determined by the type of binding used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task PublishEventAsync( + string pubsubName, + string topicName, + Dictionary metadata, + CancellationToken cancellationToken = default); + + /// + /// // Bulk Publishes multiple events to the specified topic. + /// + /// The name of the pubsub component to use. + /// The name of the topic the request should be published to. + /// The list of events to be published. + /// The metadata to be set at the request level for the request. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task> BulkPublishEventAsync( + string pubsubName, + string topicName, + IReadOnlyList events, + Dictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Publishes an event to the specified topic. + /// + /// The name of the pubsub component to use. + /// The name of the topic the request should be published to. + /// The raw byte payload to inlcude in the message. + /// The content type of the given bytes, defaults to application/json. + /// A collection of metadata key-value pairs that will be provided to the pubsub. The valid metadata keys and values are determined by the type of binding used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task PublishByteEventAsync( + string pubsubName, + string topicName, + ReadOnlyMemory data, + string dataContentType = Constants.ContentTypeApplicationJson, + Dictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Invokes an output binding. + /// + /// The type of the data that will be JSON serialized and provided as the binding payload. + /// The name of the binding to sent the event to. + /// The type of operation to perform on the binding. + /// The data that will be JSON serialized and provided as the binding payload. + /// A collection of metadata key-value pairs that will be provided to the binding. The valid metadata keys and values are determined by the type of binding used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task InvokeBindingAsync( + string bindingName, + string operation, + TRequest data, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Invokes an output binding. + /// + /// The type of the data that will be JSON serialized and provided as the binding payload. + /// The type of the data that will be JSON deserialized from the binding response. + /// The name of the binding to sent the event to. + /// The type of operation to perform on the binding. + /// The data that will be JSON serialized and provided as the binding payload. + /// A collection of metadata key-value pairs that will be provided to the binding. The valid metadata keys and values are determined by the type of binding used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task InvokeBindingAsync( + string bindingName, + string operation, + TRequest data, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Invokes a binding with the provided . This method allows for control of the binding + /// input and output using raw bytes. + /// + /// The to send. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task InvokeBindingAsync( + BindingRequest request, + CancellationToken cancellationToken = default); + + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the POST HTTP method. + /// + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// An for use with SendInvokeMethodRequestAsync. + public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName) + { + return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName); + } + + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the POST HTTP method. + /// + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, IReadOnlyCollection> queryStringParameters) + { + return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, queryStringParameters); + } - /// - /// Creates an that can be used to perform service invocation for the - /// application identified by and invokes the method specified by - /// with the HTTP method specified by . - /// - /// The to use for the invocation request. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// An for use with SendInvokeMethodRequestAsync. - public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName); - - /// - /// Creates an that can be used to perform service invocation for the - /// application identified by and invokes the method specified by - /// with the HTTP method specified by . - /// - /// The to use for the invocation request. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A collection of key/value pairs to populate the query string from. - /// An for use with SendInvokeMethodRequestAsync. - public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, - string methodName, IReadOnlyCollection> queryStringParameters); - - /// - /// Creates an that can be used to perform service invocation for the - /// application identified by and invokes the method specified by - /// with the POST HTTP method and a JSON serialized request body specified by . - /// - /// The type of the data that will be JSON serialized and provided as the request body. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be JSON serialized and provided as the request body. - /// An for use with SendInvokeMethodRequestAsync. - public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, TRequest data) - { - return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, new List>(), data); - } + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// An for use with SendInvokeMethodRequestAsync. + public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName); + + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, + string methodName, IReadOnlyCollection> queryStringParameters); + + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the POST HTTP method and a JSON serialized request body specified by . + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// An for use with SendInvokeMethodRequestAsync. + public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, TRequest data) + { + return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, new List>(), data); + } - /// - /// Creates an that can be used to perform service invocation for the - /// application identified by and invokes the method specified by - /// with the HTTP method specified by and a JSON serialized request body specified by - /// . - /// - /// The type of the data that will be JSON serialized and provided as the request body. - /// The to use for the invocation request. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be JSON serialized and provided as the request body. - /// A collection of key/value pairs to populate the query string from. - /// An for use with SendInvokeMethodRequestAsync. - public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, IReadOnlyCollection> queryStringParameters, TRequest data); + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by and a JSON serialized request body specified by + /// . + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, IReadOnlyCollection> queryStringParameters, TRequest data); - /// - /// Perform health-check of Dapr sidecar. Return 'true' if sidecar is healthy. Otherwise 'false'. - /// CheckHealthAsync handle and will return 'false' if error will occur on transport level - /// - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task CheckHealthAsync(CancellationToken cancellationToken = default); - - /// - /// Perform health-check of Dapr sidecar's outbound APIs. Return 'true' if the sidecar is healthy. Otherwise false. This method should - /// be used over when the health of Dapr is being checked before it starts. This - /// health endpoint indicates that Dapr has stood up its APIs and is currently waiting on this application to report fully healthy. - /// - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task CheckOutboundHealthAsync(CancellationToken cancellationToken = default); - - /// - /// Calls until the sidecar is reporting as healthy. If the sidecar - /// does not become healthy, an exception will be thrown. - /// - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public abstract Task WaitForSidecarAsync(CancellationToken cancellationToken = default); - - /// - /// Send a command to the Dapr Sidecar telling it to shutdown. - /// - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public abstract Task ShutdownSidecarAsync(CancellationToken cancellationToken = default); - - /// - /// Calls the sidecar's metadata endpoint which returns information including: - /// - /// - /// The sidecar's ID. - /// - /// - /// The registered/active actors if any. - /// - /// - /// Registered components including name, type, version, and information on capabilities if present. - /// - /// - /// Any extended metadata that has been set via - /// - /// - /// - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task GetMetadataAsync(CancellationToken cancellationToken = default); - - /// - /// Perform service add extended metadata to the Dapr sidecar. - /// - /// Custom attribute name - /// Custom attribute value - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task SetMetadataAsync(string attributeName, string attributeValue, CancellationToken cancellationToken = default); - - /// - /// Perform service invocation using the request provided by . The response will - /// be returned without performing any validation on the status code. - /// - /// - /// The to send. The request must be a conforming Dapr service invocation request. - /// Use the CreateInvokeMethodRequest to create service invocation requests. - /// - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task InvokeMethodWithResponseAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); + /// + /// Perform health-check of Dapr sidecar. Return 'true' if sidecar is healthy. Otherwise 'false'. + /// CheckHealthAsync handle and will return 'false' if error will occur on transport level + /// + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task CheckHealthAsync(CancellationToken cancellationToken = default); + + /// + /// Perform health-check of Dapr sidecar's outbound APIs. Return 'true' if the sidecar is healthy. Otherwise false. This method should + /// be used over when the health of Dapr is being checked before it starts. This + /// health endpoint indicates that Dapr has stood up its APIs and is currently waiting on this application to report fully healthy. + /// + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task CheckOutboundHealthAsync(CancellationToken cancellationToken = default); + + /// + /// Calls until the sidecar is reporting as healthy. If the sidecar + /// does not become healthy, an exception will be thrown. + /// + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public abstract Task WaitForSidecarAsync(CancellationToken cancellationToken = default); + + /// + /// Send a command to the Dapr Sidecar telling it to shutdown. + /// + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public abstract Task ShutdownSidecarAsync(CancellationToken cancellationToken = default); + + /// + /// Calls the sidecar's metadata endpoint which returns information including: + /// + /// + /// The sidecar's ID. + /// + /// + /// The registered/active actors if any. + /// + /// + /// Registered components including name, type, version, and information on capabilities if present. + /// + /// + /// Any extended metadata that has been set via + /// + /// + /// + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task GetMetadataAsync(CancellationToken cancellationToken = default); + + /// + /// Perform service add extended metadata to the Dapr sidecar. + /// + /// Custom attribute name + /// Custom attribute value + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task SetMetadataAsync(string attributeName, string attributeValue, CancellationToken cancellationToken = default); + + /// + /// Perform service invocation using the request provided by . The response will + /// be returned without performing any validation on the status code. + /// + /// + /// The to send. The request must be a conforming Dapr service invocation request. + /// Use the CreateInvokeMethodRequest to create service invocation requests. + /// + /// A that can be used to cancel the operation. + /// 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); + /// + /// + /// 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. - /// - /// - /// The to send. The request must be a conforming Dapr service invocation request. - /// Use the CreateInvokeMethodRequest to create service invocation requests. - /// - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public abstract Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); - - /// - /// Perform service invocation using the request provided by . If the response has a success - /// status code the body will be deserialized using JSON to a value of type ; - /// otherwise an exception will be thrown. - /// - /// The type of the data that will be JSON deserialized from the response body. - /// - /// The to send. The request must be a conforming Dapr service invocation request. - /// Use the CreateInvokeMethodRequest to create service invocation requests. - /// - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); - - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the POST HTTP method and an empty request body. - /// If the response has a non-success status code an exception will be thrown. - /// - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public Task InvokeMethodAsync( - string appId, - string methodName, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(appId, methodName); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation using the request provided by . If the response has a non-success + /// status an exception will be thrown. + /// + /// + /// The to send. The request must be a conforming Dapr service invocation request. + /// Use the CreateInvokeMethodRequest to create service invocation requests. + /// + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public abstract Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the HTTP method specified by - /// and an empty request body. If the response has a non-success status code an exception will be thrown. - /// - /// The to use for the invocation request. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public Task InvokeMethodAsync( - HttpMethod httpMethod, - string appId, - string methodName, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation using the request provided by . If the response has a success + /// status code the body will be deserialized using JSON to a value of type ; + /// otherwise an exception will be thrown. + /// + /// The type of the data that will be JSON deserialized from the response body. + /// + /// The to send. The request must be a conforming Dapr service invocation request. + /// Use the CreateInvokeMethodRequest to create service invocation requests. + /// + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the POST HTTP method - /// and a JSON serialized request body specified by . If the response has a non-success - /// status code an exception will be thrown. - /// - /// The type of the data that will be JSON serialized and provided as the request body. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be JSON serialized and provided as the request body. - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public Task InvokeMethodAsync( - string appId, - string methodName, - TRequest data, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(appId, methodName, data); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the POST HTTP method and an empty request body. + /// If the response has a non-success status code an exception will be thrown. + /// + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public Task InvokeMethodAsync( + string appId, + string methodName, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(appId, methodName); + return InvokeMethodAsync(request, cancellationToken); + } - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the HTTP method specified by - /// and a JSON serialized request body specified by . If the response has a non-success - /// status code an exception will be thrown. - /// - /// The type of the data that will be JSON serialized and provided as the request body. - /// The to use for the invocation request. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be JSON serialized and provided as the request body. - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public Task InvokeMethodAsync( - HttpMethod httpMethod, - string appId, - string methodName, - TRequest data, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the HTTP method specified by + /// and an empty request body. If the response has a non-success status code an exception will be thrown. + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public Task InvokeMethodAsync( + HttpMethod httpMethod, + string appId, + string methodName, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName); + return InvokeMethodAsync(request, cancellationToken); + } - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the POST HTTP method - /// and an empty request body. If the response has a success - /// status code the body will be deserialized using JSON to a value of type ; - /// otherwise an exception will be thrown. - /// - /// The type of the data that will be JSON deserialized from the response body. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public Task InvokeMethodAsync( - string appId, - string methodName, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(appId, methodName); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the POST HTTP method + /// and a JSON serialized request body specified by . If the response has a non-success + /// status code an exception will be thrown. + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public Task InvokeMethodAsync( + string appId, + string methodName, + TRequest data, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(appId, methodName, data); + return InvokeMethodAsync(request, cancellationToken); + } - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the HTTP method specified by - /// and an empty request body. If the response has a success - /// status code the body will be deserialized using JSON to a value of type ; - /// otherwise an exception will be thrown. - /// - /// The type of the data that will be JSON deserialized from the response body. - /// The to use for the invocation request. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public Task InvokeMethodAsync( - HttpMethod httpMethod, - string appId, - string methodName, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the HTTP method specified by + /// and a JSON serialized request body specified by . If the response has a non-success + /// status code an exception will be thrown. + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public Task InvokeMethodAsync( + HttpMethod httpMethod, + string appId, + string methodName, + TRequest data, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); + return InvokeMethodAsync(request, cancellationToken); + } - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the POST HTTP method - /// and a JSON serialized request body specified by . If the response has a success - /// status code the body will be deserialized using JSON to a value of type ; - /// otherwise an exception will be thrown. - /// - /// The type of the data that will be JSON serialized and provided as the request body. - /// The type of the data that will be JSON deserialized from the response body. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be JSON serialized and provided as the request body. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public Task InvokeMethodAsync( - string appId, - string methodName, - TRequest data, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(appId, methodName, data); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the POST HTTP method + /// and an empty request body. If the response has a success + /// status code the body will be deserialized using JSON to a value of type ; + /// otherwise an exception will be thrown. + /// + /// The type of the data that will be JSON deserialized from the response body. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public Task InvokeMethodAsync( + string appId, + string methodName, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(appId, methodName); + return InvokeMethodAsync(request, cancellationToken); + } - /// - /// Perform service invocation for the application identified by and invokes the method - /// specified by with the HTTP method specified by - /// and a JSON serialized request body specified by . If the response has a success - /// status code the body will be deserialized using JSON to a value of type ; - /// otherwise an exception will be thrown. - /// - /// The type of the data that will be JSON serialized and provided as the request body. - /// The type of the data that will be JSON deserialized from the response body. - /// The to use for the invocation request. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be JSON serialized and provided as the request body. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public Task InvokeMethodAsync( - HttpMethod httpMethod, - string appId, - string methodName, - TRequest data, - CancellationToken cancellationToken = default) - { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); - return InvokeMethodAsync(request, cancellationToken); - } + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the HTTP method specified by + /// and an empty request body. If the response has a success + /// status code the body will be deserialized using JSON to a value of type ; + /// otherwise an exception will be thrown. + /// + /// The type of the data that will be JSON deserialized from the response body. + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public Task InvokeMethodAsync( + HttpMethod httpMethod, + string appId, + string methodName, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName); + return InvokeMethodAsync(request, cancellationToken); + } + + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the POST HTTP method + /// and a JSON serialized request body specified by . If the response has a success + /// status code the body will be deserialized using JSON to a value of type ; + /// otherwise an exception will be thrown. + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The type of the data that will be JSON deserialized from the response body. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public Task InvokeMethodAsync( + string appId, + string methodName, + TRequest data, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(appId, methodName, data); + return InvokeMethodAsync(request, cancellationToken); + } - /// - /// Perform service invocation using gRPC semantics for the application identified by and invokes the method - /// specified by with an empty request body. - /// If the response has a non-success status code an exception will be thrown. - /// - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public abstract Task InvokeMethodGrpcAsync( - string appId, - string methodName, - CancellationToken cancellationToken = default); - - /// - /// Perform service invocation using gRPC semantics for the application identified by and invokes the method - /// specified by with a Protobuf serialized request body specified by . - /// If the response has a non-success status code an exception will be thrown. - /// - /// The type of the data that will be Protobuf serialized and provided as the request body. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be Protobuf serialized and provided as the request body. - /// A that can be used to cancel the operation. - /// A that will return when the operation has completed. - public abstract Task InvokeMethodGrpcAsync( - string appId, - string methodName, - TRequest data, - CancellationToken cancellationToken = default) + /// + /// Perform service invocation for the application identified by and invokes the method + /// specified by with the HTTP method specified by + /// and a JSON serialized request body specified by . If the response has a success + /// status code the body will be deserialized using JSON to a value of type ; + /// otherwise an exception will be thrown. + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The type of the data that will be JSON deserialized from the response body. + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public Task InvokeMethodAsync( + HttpMethod httpMethod, + string appId, + string methodName, + TRequest data, + CancellationToken cancellationToken = default) + { + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); + return InvokeMethodAsync(request, cancellationToken); + } + + /// + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method + /// specified by with an empty request body. + /// If the response has a non-success status code an exception will be thrown. + /// + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public abstract Task InvokeMethodGrpcAsync( + string appId, + string methodName, + CancellationToken cancellationToken = default); + + /// + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method + /// specified by with a Protobuf serialized request body specified by . + /// If the response has a non-success status code an exception will be thrown. + /// + /// The type of the data that will be Protobuf serialized and provided as the request body. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be Protobuf serialized and provided as the request body. + /// A that can be used to cancel the operation. + /// A that will return when the operation has completed. + public abstract Task InvokeMethodGrpcAsync( + string appId, + string methodName, + TRequest data, + CancellationToken cancellationToken = default) where TRequest : IMessage; - /// - /// Perform service invocation using gRPC semantics for the application identified by and invokes the method - /// specified by with an empty request body. If the response has a success - /// status code the body will be deserialized using Protobuf to a value of type ; - /// otherwise an exception will be thrown. - /// - /// The type of the data that will be Protobuf deserialized from the response body. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task InvokeMethodGrpcAsync( - string appId, - string methodName, - CancellationToken cancellationToken = default) + /// + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method + /// specified by with an empty request body. If the response has a success + /// status code the body will be deserialized using Protobuf to a value of type ; + /// otherwise an exception will be thrown. + /// + /// The type of the data that will be Protobuf deserialized from the response body. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task InvokeMethodGrpcAsync( + string appId, + string methodName, + CancellationToken cancellationToken = default) where TResponse : IMessage, new(); - /// - /// Perform service invocation using gRPC semantics for the application identified by and invokes the method - /// specified by with a Protobuf serialized request body specified by . If the response has a success - /// status code the body will be deserialized using Protobuf to a value of type ; - /// otherwise an exception will be thrown. - /// - /// The type of the data that will be Protobuf serialized and provided as the request body. - /// The type of the data that will be Protobuf deserialized from the response body. - /// The Dapr application id to invoke the method on. - /// The name of the method to invoke. - /// The data that will be Protobuf serialized and provided as the request body. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task InvokeMethodGrpcAsync( - string appId, - string methodName, - TRequest data, - CancellationToken cancellationToken = default) + /// + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method + /// specified by with a Protobuf serialized request body specified by . If the response has a success + /// status code the body will be deserialized using Protobuf to a value of type ; + /// otherwise an exception will be thrown. + /// + /// The type of the data that will be Protobuf serialized and provided as the request body. + /// The type of the data that will be Protobuf deserialized from the response body. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be Protobuf serialized and provided as the request body. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task InvokeMethodGrpcAsync( + string appId, + string methodName, + TRequest data, + CancellationToken cancellationToken = default) where TRequest : IMessage where TResponse : IMessage, new(); - /// - /// Gets the current value associated with the from the Dapr state store. - /// - /// The name of state store to read from. - /// The state key. - /// The consistency mode . - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// The data type of the value to read. - /// A that will return the value when the operation has completed. - public abstract Task GetStateAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); - - /// - /// Gets a list of values associated with the from the Dapr state store. - /// - /// The name of state store to read from. - /// The list of keys to get values for. - /// The number of concurrent get operations the Dapr runtime will issue to the state store. a value equal to or smaller than 0 means max parallelism. - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will return the list of values when the operation has completed. - public abstract Task> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); - - /// - /// Gets a list of deserialized values associated with the from the Dapr state store. This overload should be used - /// if you expect the values of all the retrieved items to match the shape of the indicated . If you expect that - /// the values may differ in type from one another, do not specify the type parameter and instead use the original method - /// so the serialized string values will be returned instead. - /// - /// The name of state store to read from. - /// The list of keys to get values for. - /// The number of concurrent get operations the Dapr runtime will issue to the state store. a value equal to or smaller than 0 means max parallelism. - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will return the list of deserialized values when the operation has completed. - public abstract Task>> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + /// + /// Gets the current value associated with the from the Dapr state store. + /// + /// The name of state store to read from. + /// The state key. + /// The consistency mode . + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// The data type of the value to read. + /// A that will return the value when the operation has completed. + public abstract Task GetStateAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + + /// + /// Gets a list of values associated with the from the Dapr state store. + /// + /// The name of state store to read from. + /// The list of keys to get values for. + /// The number of concurrent get operations the Dapr runtime will issue to the state store. a value equal to or smaller than 0 means max parallelism. + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will return the list of values when the operation has completed. + public abstract Task> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + + /// + /// Gets a list of deserialized values associated with the from the Dapr state store. This overload should be used + /// if you expect the values of all the retrieved items to match the shape of the indicated . If you expect that + /// the values may differ in type from one another, do not specify the type parameter and instead use the original method + /// so the serialized string values will be returned instead. + /// + /// The name of state store to read from. + /// The list of keys to get values for. + /// The number of concurrent get operations the Dapr runtime will issue to the state store. a value equal to or smaller than 0 means max parallelism. + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will return the list of deserialized values when the operation has completed. + public abstract Task>> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); - /// - /// Saves a list of to the Dapr state store. - /// - /// The name of state store. - /// The list of items to save. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task SaveBulkStateAsync(string storeName, IReadOnlyList> items, CancellationToken cancellationToken = default); - - /// - /// Deletes a list of from the Dapr state store. - /// - /// The name of state store to delete from. - /// The list of items to delete - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task DeleteBulkStateAsync(string storeName, IReadOnlyList items, CancellationToken cancellationToken = default); - - /// - /// Gets the current value associated with the from the Dapr state store and an ETag. - /// - /// The data type of the value to read. - /// The name of the state store. - /// The state key. - /// The consistency mode . - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. This wraps the read value and an ETag. - public abstract Task<(TValue value, string etag)> GetStateAndETagAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); - - /// - /// Gets a for the current value associated with the from - /// the Dapr state store. - /// - /// The name of the state store. - /// The state key. - /// The consistency mode . - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// The type of the data that will be JSON deserialized from the state store response. - /// A that will return the when the operation has completed. - public async Task> GetStateEntryAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); + /// + /// Saves a list of to the Dapr state store. + /// + /// The name of state store. + /// The list of items to save. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task SaveBulkStateAsync(string storeName, IReadOnlyList> items, CancellationToken cancellationToken = default); - var (state, etag) = await this.GetStateAndETagAsync(storeName, key, consistencyMode, metadata, cancellationToken); - return new StateEntry(this, storeName, key, state, etag); - } + /// + /// Deletes a list of from the Dapr state store. + /// + /// The name of state store to delete from. + /// The list of items to delete + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task DeleteBulkStateAsync(string storeName, IReadOnlyList items, CancellationToken cancellationToken = default); + + /// + /// Gets the current value associated with the from the Dapr state store and an ETag. + /// + /// The data type of the value to read. + /// The name of the state store. + /// The state key. + /// The consistency mode . + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. This wraps the read value and an ETag. + public abstract Task<(TValue value, string etag)> GetStateAndETagAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + + /// + /// Gets a for the current value associated with the from + /// the Dapr state store. + /// + /// The name of the state store. + /// The state key. + /// The consistency mode . + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// The type of the data that will be JSON deserialized from the state store response. + /// A that will return the when the operation has completed. + public async Task> GetStateEntryAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); + ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); + + var (state, etag) = await this.GetStateAndETagAsync(storeName, key, consistencyMode, metadata, cancellationToken); + return new StateEntry(this, storeName, key, state, etag); + } + + /// + /// Saves the provided associated with the provided to the Dapr state + /// store. + /// + /// The name of the state store. + /// The state key. + /// The data that will be JSON serialized and stored in the state store. + /// Options for performing save state operation. + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// The type of the data that will be JSON serialized and stored in the state store. + /// A that will complete when the operation has completed. + public abstract Task SaveStateAsync( + string storeName, + string key, + TValue value, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + + /// + /// Saves the provided associated with the provided to the Dapr state + /// store + /// + /// The name of the state store. + /// The state key. + /// The binary data that will be stored in the state store. + /// Options for performing save state operation. + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task SaveByteStateAsync( + string storeName, + string key, + ReadOnlyMemory binaryValue, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + ///Saves the provided associated with the provided using the + /// to the Dapr state. State store implementation will allow the update only if the attached ETag matches with the latest ETag in the state store. + /// + /// The name of the state store. + /// The state key. + /// The binary data that will be stored in the state store. + /// An ETag. + /// Options for performing save state operation. + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task TrySaveByteStateAsync( + string storeName, + string key, + ReadOnlyMemory binaryValue, + string etag, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + + /// + /// Gets the current binary value associated with the from the Dapr state store. + /// + /// The name of state store to read from. + /// The state key. + /// The consistency mode . + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task> GetByteStateAsync( + string storeName, + string key, + ConsistencyMode? consistencyMode = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Gets the current binary value associated with the from the Dapr state store and an ETag. + /// + /// The name of the state store. + /// The state key. + /// The consistency mode . + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. This wraps the read value and an ETag. + public abstract Task<(ReadOnlyMemory, string etag)> GetByteStateAndETagAsync( + string storeName, + string key, + ConsistencyMode? consistencyMode = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Tries to save the state associated with the provided using the + /// to the Dapr state. State store implementation will allow the update only if the attached ETag matches with the latest ETag in the state store. + /// store. + /// + /// The name of the state store. + /// The state key. + /// The data that will be JSON serialized and stored in the state store. + /// An ETag. + /// Options for performing save state operation. + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// The type of the data that will be JSON serialized and stored in the state store. + /// A that will complete when the operation has completed. If the wrapped value is true the operation succeeded. + public abstract Task TrySaveStateAsync( + string storeName, + string key, + TValue value, + string etag, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Saves the provided to the Dapr state + /// store. + /// + /// The name of the state store. + /// A list of StateTransactionRequests. + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task ExecuteStateTransactionAsync( + string storeName, + IReadOnlyList operations, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Deletes the value associated with the provided in the Dapr state store. + /// + /// The state store name. + /// The state key. + /// A . + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public abstract Task DeleteStateAsync( + string storeName, + string key, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Tries to delete the the state associated with the provided using the + /// from the Dapr state. State store implementation will allow the delete only if the attached ETag matches with the latest ETag in the state store. + /// + /// The state store name. + /// The state key. + /// An ETag. + /// A . + /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. + public abstract Task TryDeleteStateAsync( + string storeName, + string key, + string etag, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Queries the specified statestore with the given query. The query is a JSON representation of the query as described by the Dapr QueryState API. + /// Note that the underlying statestore must support queries. + /// + /// The name of the statestore. + /// A JSON formatted query string. + /// Metadata to send to the statestore. + /// A that can be used to cancel the operation. + /// The data type of the value to read. + /// A that may be paginated, use to continue the query. + public abstract Task> QueryStateAsync( + string storeName, + string jsonQuery, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Gets the secret value from the secret store. + /// + /// Secret store name. + /// Key for the secret. + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task> GetSecretAsync( + string storeName, + string key, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Gets all secret values that the application is allowed to access from the secret store. + /// + /// Secret store name. + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// A that can be used to cancel the operation. + /// A that will return the value when the operation has completed. + public abstract Task>> GetBulkSecretAsync( + string storeName, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Get a list of configuration items based on keys from the given statestore. + /// + /// The name of the configuration store to be queried. + /// An optional list of keys to query for. If provided, the result will only contain those keys. An empty list indicates all keys should be fetched. + /// Optional metadata that will be sent to the configuration store being queried. + /// A that can be used to cancel the operation. + /// A containing a + public abstract Task GetConfiguration( + string storeName, + IReadOnlyList keys, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Subscribe to a configuration store for the specified keys and receive an updated value whenever the key is updated in the store. + /// + /// The name of the configuration store to be queried. + /// An optional list of keys to query for. If provided, the result will only contain those keys. An empty list indicates all keys should be fetched. + /// Optional metadata that will be sent to the configuration store being queried. + /// A that can be used to cancel the operation. + /// A which contains a reference to the stream. + public abstract Task SubscribeConfiguration( + string storeName, + IReadOnlyList keys, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default); + + /// + /// Unsubscribe from a configuration store using the specified Id. + /// + /// The name of the configuration store. + /// The Id of the subscription that should no longer be watched. + /// A that can be used to cancel the operation. + /// + public abstract Task UnsubscribeConfiguration( + string storeName, + string id, + CancellationToken cancellationToken = default); + + #region Cryptography - /// - /// Saves the provided associated with the provided to the Dapr state - /// store. - /// - /// The name of the state store. - /// The state key. - /// The data that will be JSON serialized and stored in the state store. - /// Options for performing save state operation. - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// The type of the data that will be JSON serialized and stored in the state store. - /// A that will complete when the operation has completed. - public abstract Task SaveStateAsync( - string storeName, - string key, - TValue value, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - - /// - /// Saves the provided associated with the provided to the Dapr state - /// store - /// - /// The name of the state store. - /// The state key. - /// The binary data that will be stored in the state store. - /// Options for performing save state operation. - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task SaveByteStateAsync( - string storeName, - string key, - ReadOnlyMemory binaryValue, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - ///Saves the provided associated with the provided using the - /// to the Dapr state. State store implementation will allow the update only if the attached ETag matches with the latest ETag in the state store. - /// - /// The name of the state store. - /// The state key. - /// The binary data that will be stored in the state store. - /// An ETag. - /// Options for performing save state operation. - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task TrySaveByteStateAsync( - string storeName, - string key, - ReadOnlyMemory binaryValue, - string etag, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - - /// - /// Gets the current binary value associated with the from the Dapr state store. - /// - /// The name of state store to read from. - /// The state key. - /// The consistency mode . - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task> GetByteStateAsync( - string storeName, - string key, - ConsistencyMode? consistencyMode = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Gets the current binary value associated with the from the Dapr state store and an ETag. - /// - /// The name of the state store. - /// The state key. - /// The consistency mode . - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. This wraps the read value and an ETag. - public abstract Task<(ReadOnlyMemory, string etag)> GetByteStateAndETagAsync( - string storeName, - string key, - ConsistencyMode? consistencyMode = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Tries to save the state associated with the provided using the - /// to the Dapr state. State store implementation will allow the update only if the attached ETag matches with the latest ETag in the state store. - /// store. - /// - /// The name of the state store. - /// The state key. - /// The data that will be JSON serialized and stored in the state store. - /// An ETag. - /// Options for performing save state operation. - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// The type of the data that will be JSON serialized and stored in the state store. - /// A that will complete when the operation has completed. If the wrapped value is true the operation succeeded. - public abstract Task TrySaveStateAsync( - string storeName, - string key, - TValue value, - string etag, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Saves the provided to the Dapr state - /// store. - /// - /// The name of the state store. - /// A list of StateTransactionRequests. - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task ExecuteStateTransactionAsync( - string storeName, - IReadOnlyList operations, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Deletes the value associated with the provided in the Dapr state store. - /// - /// The state store name. - /// The state key. - /// A . - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public abstract Task DeleteStateAsync( - string storeName, - string key, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Tries to delete the the state associated with the provided using the - /// from the Dapr state. State store implementation will allow the delete only if the attached ETag matches with the latest ETag in the state store. - /// - /// The state store name. - /// The state key. - /// An ETag. - /// A . - /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. - public abstract Task TryDeleteStateAsync( - string storeName, - string key, - string etag, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Queries the specified statestore with the given query. The query is a JSON representation of the query as described by the Dapr QueryState API. - /// Note that the underlying statestore must support queries. - /// - /// The name of the statestore. - /// A JSON formatted query string. - /// Metadata to send to the statestore. - /// A that can be used to cancel the operation. - /// The data type of the value to read. - /// A that may be paginated, use to continue the query. - public abstract Task> QueryStateAsync( - string storeName, - string jsonQuery, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Gets the secret value from the secret store. - /// - /// Secret store name. - /// Key for the secret. - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task> GetSecretAsync( - string storeName, - string key, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Gets all secret values that the application is allowed to access from the secret store. - /// - /// Secret store name. - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// A that can be used to cancel the operation. - /// A that will return the value when the operation has completed. - public abstract Task>> GetBulkSecretAsync( - string storeName, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Get a list of configuration items based on keys from the given statestore. - /// - /// The name of the configuration store to be queried. - /// An optional list of keys to query for. If provided, the result will only contain those keys. An empty list indicates all keys should be fetched. - /// Optional metadata that will be sent to the configuration store being queried. - /// A that can be used to cancel the operation. - /// A containing a - public abstract Task GetConfiguration( - string storeName, - IReadOnlyList keys, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Subscribe to a configuration store for the specified keys and receive an updated value whenever the key is updated in the store. - /// - /// The name of the configuration store to be queried. - /// An optional list of keys to query for. If provided, the result will only contain those keys. An empty list indicates all keys should be fetched. - /// Optional metadata that will be sent to the configuration store being queried. - /// A that can be used to cancel the operation. - /// A which contains a reference to the stream. - public abstract Task SubscribeConfiguration( - string storeName, - IReadOnlyList keys, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default); - - /// - /// Unsubscribe from a configuration store using the specified Id. - /// - /// The name of the configuration store. - /// The Id of the subscription that should no longer be watched. - /// A that can be used to cancel the operation. - /// - public abstract Task UnsubscribeConfiguration( - string storeName, - string id, - CancellationToken cancellationToken = default); - - #region Cryptography - - /// - /// Encrypts an array of bytes using the Dapr Cryptography encryption functionality. - /// - /// The name of the vault resource used by the operation. - /// The bytes of the plaintext value to encrypt. - /// The name of the key to use from the Vault for the encryption operation. - /// Options informing how the encryption operation should be configured. - /// A that can be used to cancel the operation. - /// An array of encrypted bytes. - [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task> EncryptAsync(string vaultResourceName, - ReadOnlyMemory plaintextBytes, string keyName, EncryptionOptions encryptionOptions, - CancellationToken cancellationToken = default); - - /// - /// Encrypts a stream using the Dapr Cryptography encryption functionality. - /// - /// The name of the vault resource used by the operation. - /// The stream containing the bytes of the plaintext value to encrypt. - /// The name of the key to use from the Vault for the encryption operation. - /// Options informing how the encryption operation should be configured. - /// A that can be used to cancel the operation. - /// An array of encrypted bytes. - [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName, - EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default); - - /// - /// Decrypts the specified ciphertext bytes using the Dapr Cryptography encryption functionality. - /// - /// The name of the vault resource used by the operation. - /// The bytes of the ciphertext value to decrypt. - /// The name of the key to use from the Vault for the decryption operation. - /// Options informing how the decryption operation should be configured. - /// A that can be used to cancel the operation. - /// An array of decrypted bytes. - [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task> DecryptAsync(string vaultResourceName, ReadOnlyMemory ciphertextBytes, string keyName, DecryptionOptions options, - CancellationToken cancellationToken = default); - - /// - /// Decrypts the specified ciphertext bytes using the Dapr Cryptography encryption functionality. - /// - /// The name of the vault resource used by the operation. - /// The bytes of the ciphertext value to decrypt. - /// The name of the key to use from the Vault for the decryption operation. - /// A that can be used to cancel the operation. - /// An array of decrypted bytes. - [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task> DecryptAsync(string vaultResourceName, - ReadOnlyMemory ciphertextBytes, string keyName, CancellationToken cancellationToken = default); - - /// - /// Decrypts the specified stream of ciphertext using the Dapr Cryptography encryption functionality. - /// - /// The name of the vault resource used by the operation. - /// The stream containing the bytes of the ciphertext value to decrypt. - /// The name of the key to use from the Vault for the decryption operation. - /// Options informing how the decryption operation should be configured. - /// A that can be used to cancel the operation. - /// An asynchronously enumerable array of decrypted bytes. - [Obsolete( - "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task>> DecryptAsync(string vaultResourceName, Stream ciphertextStream, - string keyName, DecryptionOptions options, CancellationToken cancellationToken = default); - - /// - /// Decrypts the specified stream of ciphertext using the Dapr Cryptography encryption functionality. - /// - /// The name of the vault resource used by the operation. - /// The stream containing the bytes of the ciphertext value to decrypt. - /// The name of the key to use from the Vault for the decryption operation. - /// A that can be used to cancel the operation. - /// An asynchronously enumerable array of decrypted bytes. - [Obsolete( - "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task>> DecryptAsync(string vaultResourceName, Stream ciphertextStream, - string keyName, CancellationToken cancellationToken = default); - - #endregion - - #region Cryptography - Subtle API - - ///// - ///// Retrieves the value of the specified key from the vault. - ///// - ///// The name of the vault resource used by the operation. - ///// The name of the key to retrieve the value of. - ///// The format to use for the key result. - ///// A that can be used to cancel the operation. - ///// The name (and possibly version as name/version) of the key and its public key. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public abstract Task<(string Name, string PublicKey)> GetKeyAsync(string vaultResourceName, string keyName, SubtleGetKeyRequest.Types.KeyFormat keyFormat, - // CancellationToken cancellationToken = default); - - ///// - ///// Encrypts an array of bytes using the Dapr Cryptography functionality. - ///// - ///// The name of the vault resource used by the operation. - ///// The bytes of the plaintext value to encrypt. - ///// The name of the algorithm that should be used to perform the encryption. - ///// The name of the key used to perform the encryption operation. - ///// The bytes comprising the nonce. - ///// Any associated data when using AEAD ciphers. - ///// A that can be used to cancel the operation. - ///// The array of encrypted bytes. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public abstract Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync( - // string vaultResourceName, - // byte[] plainTextBytes, - // string algorithm, - // string keyName, - // byte[] nonce, - // byte[] associatedData, - // CancellationToken cancellationToken = default); - - ///// - ///// Encrypts an array of bytes using the Dapr Cryptography functionality. - ///// - ///// The name of the vault resource used by the operation. - ///// The bytes of the plaintext value to encrypt. - ///// The name of the algorithm that should be used to perform the encryption. - ///// The name of the key used to perform the encryption operation. - ///// The bytes comprising the nonce. - ///// A that can be used to cancel the operation. - ///// The array of encrypted bytes. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public async Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync( - // string vaultResourceName, - // byte[] plainTextBytes, - // string algorithm, - // string keyName, - // byte[] nonce, - // CancellationToken cancellationToken = default) => - // await EncryptAsync(vaultResourceName, plainTextBytes, algorithm, keyName, nonce, Array.Empty(), - // cancellationToken); - - ///// - ///// Decrypts an array of bytes using the Dapr Cryptography functionality. - ///// - ///// The name of the vault the key is retrieved from for the decryption operation. - ///// The array of bytes to decrypt. - ///// A that can be used to cancel the operation. - ///// The algorithm to use to perform the decryption operation. - ///// The name of the key used for the decryption. - ///// The nonce value used. - ///// - ///// Any associated data when using AEAD ciphers. - ///// The array of plaintext bytes. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public abstract Task DecryptAsync(string vaultResourceName, byte[] cipherTextBytes, - // string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData, - // CancellationToken cancellationToken = default); - - ///// - ///// Decrypts an array of bytes using the Dapr Cryptography functionality. - ///// - ///// The name of the vault the key is retrieved from for the decryption operation. - ///// The array of bytes to decrypt. - ///// A that can be used to cancel the operation. - ///// The algorithm to use to perform the decryption operation. - ///// The name of the key used for the decryption. - ///// The nonce value used. - ///// - ///// The array of plaintext bytes. - //[Obsolete( - // "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public async Task DecryptAsync(string vaultResourceName, byte[] cipherTextBytes, - // string algorithm, string keyName, byte[] nonce, byte[] tag, CancellationToken cancellationToken = default) => - // await DecryptAsync(vaultResourceName, cipherTextBytes, algorithm, keyName, nonce, tag, Array.Empty(), cancellationToken); - - ///// - ///// Wraps the plaintext key using another. - ///// - ///// The name of the vault to retrieve the key from. - ///// The plaintext bytes comprising the key to wrap. - ///// The name of the key used to wrap the value. - ///// The algorithm to use to perform the wrap operation. - ///// The none used. - ///// Any associated data when using AEAD ciphers. - ///// A that can be used to cancel the operation. - ///// The bytes comprising the wrapped plain-text key and the authentication tag, if applicable. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public abstract Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm, byte[] nonce, byte[] associatedData, - // CancellationToken cancellationToken = default); - - ///// - ///// Wraps the plaintext key using another. - ///// - ///// The name of the vault to retrieve the key from. - ///// The plaintext bytes comprising the key to wrap. - ///// The name of the key used to wrap the value. - ///// The algorithm to use to perform the wrap operation. - ///// The none used. - ///// A that can be used to cancel the operation. - ///// The bytes comprising the unwrapped key and the authentication tag, if applicable. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public async Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm, - // byte[] nonce, CancellationToken cancellationToken = default) => await WrapKeyAsync(vaultResourceName, plainTextKey, - // keyName, algorithm, nonce, Array.Empty(), cancellationToken); - - ///// - ///// Used to unwrap the specified key. - ///// - ///// The name of the vault to retrieve the key from. - ///// The byte comprising the wrapped key. - ///// The algorithm to use in unwrapping the key. - ///// The name of the key used to unwrap the wrapped key bytes. - ///// The nonce value. - ///// The bytes comprising the authentication tag. - ///// Any associated data when using AEAD ciphers. - ///// A that can be used to cancel the operation. - ///// The bytes comprising the unwrapped key. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public abstract Task UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData, - // CancellationToken cancellationToken = default); - - ///// - ///// Used to unwrap the specified key. - ///// - ///// The name of the vault to retrieve the key from. - ///// The byte comprising the wrapped key. - ///// The algorithm to use in unwrapping the key. - ///// The name of the key used to unwrap the wrapped key bytes. - ///// The nonce value. - ///// The bytes comprising the authentication tag. - ///// A that can be used to cancel the operation. - ///// The bytes comprising the unwrapped key. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public async Task UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, - // byte[] nonce, byte[] tag, - // CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName, - // wrappedKey, algorithm, keyName, nonce, Array.Empty(), Array.Empty(), cancellationToken); - - ///// - ///// Used to unwrap the specified key. - ///// - ///// The name of the vault to retrieve the key from. - ///// The byte comprising the wrapped key. - ///// The algorithm to use in unwrapping the key. - ///// The name of the key used to unwrap the wrapped key bytes. - ///// The nonce value. - ///// A that can be used to cancel the operation. - ///// The bytes comprising the unwrapped key. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public async Task UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, - // byte[] nonce, CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName, - // wrappedKey, algorithm, keyName, nonce, Array.Empty(), Array.Empty(), cancellationToken); - - ///// - ///// Creates a signature of a digest value. - ///// - ///// The name of the vault to retrieve the key from. - ///// The digest value to create the signature for. - ///// The algorithm used to create the signature. - ///// The name of the key used. - ///// A that can be used to cancel the operation. - ///// The bytes comprising the signature. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public abstract Task SignAsync(string vaultResourceName, byte[] digest, string algorithm, string keyName, - // CancellationToken cancellationToken = default); - - ///// - ///// Validates a signature. - ///// - ///// The name of the vault to retrieve the key from. - ///// The digest to validate the signature with. - ///// The signature to validate. - ///// The algorithm to validate the signature with. - ///// The name of the key used. - ///// A that can be used to cancel the operation. - ///// True if the signature verification is successful; otherwise false. - //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - //public abstract Task VerifyAsync(string vaultResourceName, byte[] digest, byte[] signature, string algorithm, string keyName, - // CancellationToken cancellationToken = default); - - #endregion - - /// - /// Attempt to lock the given resourceId with response indicating success. - /// - /// The name of the lock store to be queried. - /// Lock key that stands for which resource to protect. - /// Indicates the identifier of lock owner. - /// The time after which the lock gets expired. - /// A that can be used to cancel the operation. - /// A containing a - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task Lock( - string storeName, - string resourceId, - string lockOwner, - Int32 expiryInSeconds, - CancellationToken cancellationToken = default); - - - /// - /// Attempt to unlock the given resourceId with response indicating success. - /// - /// The name of the lock store to be queried. - /// Lock key that stands for which resource to protect. - /// Indicates the identifier of lock owner. - /// A that can be used to cancel the operation. - /// A containing a - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task Unlock( - string storeName, - string resourceId, - string lockOwner, - CancellationToken cancellationToken = default); + /// + /// Encrypts an array of bytes using the Dapr Cryptography encryption functionality. + /// + /// The name of the vault resource used by the operation. + /// The bytes of the plaintext value to encrypt. + /// The name of the key to use from the Vault for the encryption operation. + /// Options informing how the encryption operation should be configured. + /// A that can be used to cancel the operation. + /// An array of encrypted bytes. + [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task> EncryptAsync(string vaultResourceName, + ReadOnlyMemory plaintextBytes, string keyName, EncryptionOptions encryptionOptions, + CancellationToken cancellationToken = default); + + /// + /// Encrypts a stream using the Dapr Cryptography encryption functionality. + /// + /// The name of the vault resource used by the operation. + /// The stream containing the bytes of the plaintext value to encrypt. + /// The name of the key to use from the Vault for the encryption operation. + /// Options informing how the encryption operation should be configured. + /// A that can be used to cancel the operation. + /// An array of encrypted bytes. + [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName, + EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default); + + /// + /// Decrypts the specified ciphertext bytes using the Dapr Cryptography encryption functionality. + /// + /// The name of the vault resource used by the operation. + /// The bytes of the ciphertext value to decrypt. + /// The name of the key to use from the Vault for the decryption operation. + /// Options informing how the decryption operation should be configured. + /// A that can be used to cancel the operation. + /// An array of decrypted bytes. + [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task> DecryptAsync(string vaultResourceName, ReadOnlyMemory ciphertextBytes, string keyName, DecryptionOptions options, + CancellationToken cancellationToken = default); + + /// + /// Decrypts the specified ciphertext bytes using the Dapr Cryptography encryption functionality. + /// + /// The name of the vault resource used by the operation. + /// The bytes of the ciphertext value to decrypt. + /// The name of the key to use from the Vault for the decryption operation. + /// A that can be used to cancel the operation. + /// An array of decrypted bytes. + [Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task> DecryptAsync(string vaultResourceName, + ReadOnlyMemory ciphertextBytes, string keyName, CancellationToken cancellationToken = default); + + /// + /// Decrypts the specified stream of ciphertext using the Dapr Cryptography encryption functionality. + /// + /// The name of the vault resource used by the operation. + /// The stream containing the bytes of the ciphertext value to decrypt. + /// The name of the key to use from the Vault for the decryption operation. + /// Options informing how the decryption operation should be configured. + /// A that can be used to cancel the operation. + /// An asynchronously enumerable array of decrypted bytes. + [Obsolete( + "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task>> DecryptAsync(string vaultResourceName, Stream ciphertextStream, + string keyName, DecryptionOptions options, CancellationToken cancellationToken = default); + + /// + /// Decrypts the specified stream of ciphertext using the Dapr Cryptography encryption functionality. + /// + /// The name of the vault resource used by the operation. + /// The stream containing the bytes of the ciphertext value to decrypt. + /// The name of the key to use from the Vault for the decryption operation. + /// A that can be used to cancel the operation. + /// An asynchronously enumerable array of decrypted bytes. + [Obsolete( + "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task>> DecryptAsync(string vaultResourceName, Stream ciphertextStream, + string keyName, CancellationToken cancellationToken = default); + + #endregion + + #region Cryptography - Subtle API + + ///// + ///// Retrieves the value of the specified key from the vault. + ///// + ///// The name of the vault resource used by the operation. + ///// The name of the key to retrieve the value of. + ///// The format to use for the key result. + ///// A that can be used to cancel the operation. + ///// The name (and possibly version as name/version) of the key and its public key. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public abstract Task<(string Name, string PublicKey)> GetKeyAsync(string vaultResourceName, string keyName, SubtleGetKeyRequest.Types.KeyFormat keyFormat, + // CancellationToken cancellationToken = default); + + ///// + ///// Encrypts an array of bytes using the Dapr Cryptography functionality. + ///// + ///// The name of the vault resource used by the operation. + ///// The bytes of the plaintext value to encrypt. + ///// The name of the algorithm that should be used to perform the encryption. + ///// The name of the key used to perform the encryption operation. + ///// The bytes comprising the nonce. + ///// Any associated data when using AEAD ciphers. + ///// A that can be used to cancel the operation. + ///// The array of encrypted bytes. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public abstract Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync( + // string vaultResourceName, + // byte[] plainTextBytes, + // string algorithm, + // string keyName, + // byte[] nonce, + // byte[] associatedData, + // CancellationToken cancellationToken = default); + + ///// + ///// Encrypts an array of bytes using the Dapr Cryptography functionality. + ///// + ///// The name of the vault resource used by the operation. + ///// The bytes of the plaintext value to encrypt. + ///// The name of the algorithm that should be used to perform the encryption. + ///// The name of the key used to perform the encryption operation. + ///// The bytes comprising the nonce. + ///// A that can be used to cancel the operation. + ///// The array of encrypted bytes. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public async Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync( + // string vaultResourceName, + // byte[] plainTextBytes, + // string algorithm, + // string keyName, + // byte[] nonce, + // CancellationToken cancellationToken = default) => + // await EncryptAsync(vaultResourceName, plainTextBytes, algorithm, keyName, nonce, Array.Empty(), + // cancellationToken); + + ///// + ///// Decrypts an array of bytes using the Dapr Cryptography functionality. + ///// + ///// The name of the vault the key is retrieved from for the decryption operation. + ///// The array of bytes to decrypt. + ///// A that can be used to cancel the operation. + ///// The algorithm to use to perform the decryption operation. + ///// The name of the key used for the decryption. + ///// The nonce value used. + ///// + ///// Any associated data when using AEAD ciphers. + ///// The array of plaintext bytes. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public abstract Task DecryptAsync(string vaultResourceName, byte[] cipherTextBytes, + // string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData, + // CancellationToken cancellationToken = default); + + ///// + ///// Decrypts an array of bytes using the Dapr Cryptography functionality. + ///// + ///// The name of the vault the key is retrieved from for the decryption operation. + ///// The array of bytes to decrypt. + ///// A that can be used to cancel the operation. + ///// The algorithm to use to perform the decryption operation. + ///// The name of the key used for the decryption. + ///// The nonce value used. + ///// + ///// The array of plaintext bytes. + //[Obsolete( + // "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public async Task DecryptAsync(string vaultResourceName, byte[] cipherTextBytes, + // string algorithm, string keyName, byte[] nonce, byte[] tag, CancellationToken cancellationToken = default) => + // await DecryptAsync(vaultResourceName, cipherTextBytes, algorithm, keyName, nonce, tag, Array.Empty(), cancellationToken); + + ///// + ///// Wraps the plaintext key using another. + ///// + ///// The name of the vault to retrieve the key from. + ///// The plaintext bytes comprising the key to wrap. + ///// The name of the key used to wrap the value. + ///// The algorithm to use to perform the wrap operation. + ///// The none used. + ///// Any associated data when using AEAD ciphers. + ///// A that can be used to cancel the operation. + ///// The bytes comprising the wrapped plain-text key and the authentication tag, if applicable. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public abstract Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm, byte[] nonce, byte[] associatedData, + // CancellationToken cancellationToken = default); + + ///// + ///// Wraps the plaintext key using another. + ///// + ///// The name of the vault to retrieve the key from. + ///// The plaintext bytes comprising the key to wrap. + ///// The name of the key used to wrap the value. + ///// The algorithm to use to perform the wrap operation. + ///// The none used. + ///// A that can be used to cancel the operation. + ///// The bytes comprising the unwrapped key and the authentication tag, if applicable. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public async Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm, + // byte[] nonce, CancellationToken cancellationToken = default) => await WrapKeyAsync(vaultResourceName, plainTextKey, + // keyName, algorithm, nonce, Array.Empty(), cancellationToken); + + ///// + ///// Used to unwrap the specified key. + ///// + ///// The name of the vault to retrieve the key from. + ///// The byte comprising the wrapped key. + ///// The algorithm to use in unwrapping the key. + ///// The name of the key used to unwrap the wrapped key bytes. + ///// The nonce value. + ///// The bytes comprising the authentication tag. + ///// Any associated data when using AEAD ciphers. + ///// A that can be used to cancel the operation. + ///// The bytes comprising the unwrapped key. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public abstract Task UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData, + // CancellationToken cancellationToken = default); + + ///// + ///// Used to unwrap the specified key. + ///// + ///// The name of the vault to retrieve the key from. + ///// The byte comprising the wrapped key. + ///// The algorithm to use in unwrapping the key. + ///// The name of the key used to unwrap the wrapped key bytes. + ///// The nonce value. + ///// The bytes comprising the authentication tag. + ///// A that can be used to cancel the operation. + ///// The bytes comprising the unwrapped key. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public async Task UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, + // byte[] nonce, byte[] tag, + // CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName, + // wrappedKey, algorithm, keyName, nonce, Array.Empty(), Array.Empty(), cancellationToken); + + ///// + ///// Used to unwrap the specified key. + ///// + ///// The name of the vault to retrieve the key from. + ///// The byte comprising the wrapped key. + ///// The algorithm to use in unwrapping the key. + ///// The name of the key used to unwrap the wrapped key bytes. + ///// The nonce value. + ///// A that can be used to cancel the operation. + ///// The bytes comprising the unwrapped key. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public async Task UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, + // byte[] nonce, CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName, + // wrappedKey, algorithm, keyName, nonce, Array.Empty(), Array.Empty(), cancellationToken); + + ///// + ///// Creates a signature of a digest value. + ///// + ///// The name of the vault to retrieve the key from. + ///// The digest value to create the signature for. + ///// The algorithm used to create the signature. + ///// The name of the key used. + ///// A that can be used to cancel the operation. + ///// The bytes comprising the signature. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public abstract Task SignAsync(string vaultResourceName, byte[] digest, string algorithm, string keyName, + // CancellationToken cancellationToken = default); + + ///// + ///// Validates a signature. + ///// + ///// The name of the vault to retrieve the key from. + ///// The digest to validate the signature with. + ///// The signature to validate. + ///// The algorithm to validate the signature with. + ///// The name of the key used. + ///// A that can be used to cancel the operation. + ///// True if the signature verification is successful; otherwise false. + //[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + //public abstract Task VerifyAsync(string vaultResourceName, byte[] digest, byte[] signature, string algorithm, string keyName, + // CancellationToken cancellationToken = default); + + #endregion + + /// + /// Attempt to lock the given resourceId with response indicating success. + /// + /// The name of the lock store to be queried. + /// Lock key that stands for which resource to protect. + /// Indicates the identifier of lock owner. + /// The time after which the lock gets expired. + /// A that can be used to cancel the operation. + /// A containing a + [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task Lock( + string storeName, + string resourceId, + string lockOwner, + Int32 expiryInSeconds, + CancellationToken cancellationToken = default); + + + /// + /// Attempt to unlock the given resourceId with response indicating success. + /// + /// The name of the lock store to be queried. + /// Lock key that stands for which resource to protect. + /// Indicates the identifier of lock owner. + /// A that can be used to cancel the operation. + /// A containing a + [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] + public abstract Task Unlock( + string storeName, + string resourceId, + string lockOwner, + CancellationToken cancellationToken = default); - /// - public void Dispose() + /// + public void Dispose() + { + if (!this.disposed) { - if (!this.disposed) - { - Dispose(disposing: true); - this.disposed = true; - } + Dispose(disposing: true); + this.disposed = true; } + } - /// - /// Disposes the resources associated with the object. - /// - /// true if called by a call to the Dispose method; otherwise false. - protected virtual void Dispose(bool disposing) - { - } + /// + /// Disposes the resources associated with the object. + /// + /// true if called by a call to the Dispose method; otherwise false. + protected virtual void Dispose(bool disposing) + { + } - /// - /// Returns the value for the User-Agent. - /// - /// A containing the value to use for User-Agent. - protected static ProductInfoHeaderValue UserAgent() - { - var assembly = typeof(DaprClient).Assembly; - string assemblyVersion = assembly - .GetCustomAttributes() - .FirstOrDefault()? - .InformationalVersion; + /// + /// Returns the value for the User-Agent. + /// + /// A containing the value to use for User-Agent. + protected static ProductInfoHeaderValue UserAgent() + { + var assembly = typeof(DaprClient).Assembly; + string assemblyVersion = assembly + .GetCustomAttributes() + .FirstOrDefault()? + .InformationalVersion; - return new ProductInfoHeaderValue("dapr-sdk-dotnet", $"v{assemblyVersion}"); - } + return new ProductInfoHeaderValue("dapr-sdk-dotnet", $"v{assemblyVersion}"); } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/DaprClientBuilder.cs b/src/Dapr.Client/DaprClientBuilder.cs index 68315c45b..ad3fe6b0f 100644 --- a/src/Dapr.Client/DaprClientBuilder.cs +++ b/src/Dapr.Client/DaprClientBuilder.cs @@ -11,180 +11,179 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +using System; +using System.Net.Http; +using System.Text.Json; +using Grpc.Net.Client; +using Autogenerated = Autogen.Grpc.v1; + +/// +/// Builder for building +/// +public sealed class DaprClientBuilder { - using System; - using System.Net.Http; - using System.Text.Json; - using Grpc.Net.Client; - using Autogenerated = Autogen.Grpc.v1; + /// + /// Initializes a new instance of the class. + /// + public DaprClientBuilder() + { + this.GrpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); + this.HttpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(); + + this.GrpcChannelOptions = new GrpcChannelOptions() + { + // The gRPC client doesn't throw the right exception for cancellation + // by default, this switches that behavior on. + ThrowOperationCanceledOnCancellation = true, + }; + + this.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); + this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(null); + } + + // property exposed for testing purposes + internal string GrpcEndpoint { get; private set; } + + // property exposed for testing purposes + internal string HttpEndpoint { get; private set; } + + private Func HttpClientFactory { get; set; } + + // property exposed for testing purposes + internal JsonSerializerOptions JsonSerializerOptions { get; private set; } + + // property exposed for testing purposes + internal GrpcChannelOptions GrpcChannelOptions { get; private set; } + internal string DaprApiToken { get; private set; } + internal TimeSpan Timeout { get; private set; } /// - /// Builder for building + /// Overrides the HTTP endpoint used by for communicating with the Dapr runtime. /// - public sealed class DaprClientBuilder + /// + /// The URI endpoint to use for HTTP calls to the Dapr runtime. The default value will be + /// DAPR_HTTP_ENDPOINT first, or http://127.0.0.1:DAPR_HTTP_PORT as fallback + /// where DAPR_HTTP_ENDPOINT and DAPR_HTTP_PORT represents the value of the + /// corresponding environment variables. + /// + /// The instance. + public DaprClientBuilder UseHttpEndpoint(string httpEndpoint) { - /// - /// Initializes a new instance of the class. - /// - public DaprClientBuilder() - { - this.GrpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); - this.HttpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(); - - this.GrpcChannelOptions = new GrpcChannelOptions() - { - // The gRPC client doesn't throw the right exception for cancellation - // by default, this switches that behavior on. - ThrowOperationCanceledOnCancellation = true, - }; - - this.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); - this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(null); - } + ArgumentVerifier.ThrowIfNullOrEmpty(httpEndpoint, nameof(httpEndpoint)); + this.HttpEndpoint = httpEndpoint; + return this; + } - // property exposed for testing purposes - internal string GrpcEndpoint { get; private set; } - - // property exposed for testing purposes - internal string HttpEndpoint { get; private set; } - - private Func HttpClientFactory { get; set; } - - // property exposed for testing purposes - internal JsonSerializerOptions JsonSerializerOptions { get; private set; } - - // property exposed for testing purposes - internal GrpcChannelOptions GrpcChannelOptions { get; private set; } - internal string DaprApiToken { get; private set; } - internal TimeSpan Timeout { get; private set; } - - /// - /// Overrides the HTTP endpoint used by for communicating with the Dapr runtime. - /// - /// - /// The URI endpoint to use for HTTP calls to the Dapr runtime. The default value will be - /// DAPR_HTTP_ENDPOINT first, or http://127.0.0.1:DAPR_HTTP_PORT as fallback - /// where DAPR_HTTP_ENDPOINT and DAPR_HTTP_PORT represents the value of the - /// corresponding environment variables. - /// - /// The instance. - public DaprClientBuilder UseHttpEndpoint(string httpEndpoint) - { - ArgumentVerifier.ThrowIfNullOrEmpty(httpEndpoint, nameof(httpEndpoint)); - this.HttpEndpoint = httpEndpoint; - return this; - } + // Internal for testing of DaprClient + internal DaprClientBuilder UseHttpClientFactory(Func factory) + { + this.HttpClientFactory = factory; + return this; + } - // Internal for testing of DaprClient - internal DaprClientBuilder UseHttpClientFactory(Func factory) - { - this.HttpClientFactory = factory; - return this; - } + /// + /// Overrides the gRPC endpoint used by for communicating with the Dapr runtime. + /// + /// + /// The URI endpoint to use for gRPC calls to the Dapr runtime. The default value will be + /// http://127.0.0.1:DAPR_GRPC_PORT where DAPR_GRPC_PORT represents the value of the + /// DAPR_GRPC_PORT environment variable. + /// + /// The instance. + public DaprClientBuilder UseGrpcEndpoint(string grpcEndpoint) + { + ArgumentVerifier.ThrowIfNullOrEmpty(grpcEndpoint, nameof(grpcEndpoint)); + this.GrpcEndpoint = grpcEndpoint; + return this; + } - /// - /// Overrides the gRPC endpoint used by for communicating with the Dapr runtime. - /// - /// - /// The URI endpoint to use for gRPC calls to the Dapr runtime. The default value will be - /// http://127.0.0.1:DAPR_GRPC_PORT where DAPR_GRPC_PORT represents the value of the - /// DAPR_GRPC_PORT environment variable. - /// - /// The instance. - public DaprClientBuilder UseGrpcEndpoint(string grpcEndpoint) - { - ArgumentVerifier.ThrowIfNullOrEmpty(grpcEndpoint, nameof(grpcEndpoint)); - this.GrpcEndpoint = grpcEndpoint; - return this; - } + /// + /// + /// Uses the specified when serializing or deserializing using . + /// + /// + /// The default value is created using . + /// + /// + /// Json serialization options. + /// The instance. + public DaprClientBuilder UseJsonSerializationOptions(JsonSerializerOptions options) + { + this.JsonSerializerOptions = options; + return this; + } - /// - /// - /// Uses the specified when serializing or deserializing using . - /// - /// - /// The default value is created using . - /// - /// - /// Json serialization options. - /// The instance. - public DaprClientBuilder UseJsonSerializationOptions(JsonSerializerOptions options) - { - this.JsonSerializerOptions = options; - return this; - } + /// + /// Uses the provided for creating the . + /// + /// The to use for creating the . + /// The instance. + public DaprClientBuilder UseGrpcChannelOptions(GrpcChannelOptions grpcChannelOptions) + { + this.GrpcChannelOptions = grpcChannelOptions; + return this; + } + + /// + /// Adds the provided on every request to the Dapr runtime. + /// + /// The token to be added to the request headers/>. + /// The instance. + public DaprClientBuilder UseDaprApiToken(string apiToken) + { + this.DaprApiToken = apiToken; + return this; + } + + /// + /// Sets the timeout for the HTTP client used by the . + /// + /// + /// + public DaprClientBuilder UseTimeout(TimeSpan timeout) + { + this.Timeout = timeout; + return this; + } - /// - /// Uses the provided for creating the . - /// - /// The to use for creating the . - /// The instance. - public DaprClientBuilder UseGrpcChannelOptions(GrpcChannelOptions grpcChannelOptions) + /// + /// Builds a instance from the properties of the builder. + /// + /// The . + public DaprClient Build() + { + var grpcEndpoint = new Uri(this.GrpcEndpoint); + if (grpcEndpoint.Scheme != "http" && grpcEndpoint.Scheme != "https") { - this.GrpcChannelOptions = grpcChannelOptions; - return this; + throw new InvalidOperationException("The gRPC endpoint must use http or https."); } - /// - /// Adds the provided on every request to the Dapr runtime. - /// - /// The token to be added to the request headers/>. - /// The instance. - public DaprClientBuilder UseDaprApiToken(string apiToken) + if (grpcEndpoint.Scheme.Equals(Uri.UriSchemeHttp)) { - this.DaprApiToken = apiToken; - return this; + // Set correct switch to maksecure gRPC service calls. This switch must be set before creating the GrpcChannel. + AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); } - /// - /// Sets the timeout for the HTTP client used by the . - /// - /// - /// - public DaprClientBuilder UseTimeout(TimeSpan timeout) + var httpEndpoint = new Uri(this.HttpEndpoint); + if (httpEndpoint.Scheme != "http" && httpEndpoint.Scheme != "https") { - this.Timeout = timeout; - return this; + throw new InvalidOperationException("The HTTP endpoint must use http or https."); } - /// - /// Builds a instance from the properties of the builder. - /// - /// The . - public DaprClient Build() - { - var grpcEndpoint = new Uri(this.GrpcEndpoint); - if (grpcEndpoint.Scheme != "http" && grpcEndpoint.Scheme != "https") - { - throw new InvalidOperationException("The gRPC endpoint must use http or https."); - } - - if (grpcEndpoint.Scheme.Equals(Uri.UriSchemeHttp)) - { - // Set correct switch to maksecure gRPC service calls. This switch must be set before creating the GrpcChannel. - AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - } - - var httpEndpoint = new Uri(this.HttpEndpoint); - if (httpEndpoint.Scheme != "http" && httpEndpoint.Scheme != "https") - { - throw new InvalidOperationException("The HTTP endpoint must use http or https."); - } - - var channel = GrpcChannel.ForAddress(this.GrpcEndpoint, this.GrpcChannelOptions); - var client = new Autogenerated.Dapr.DaprClient(channel); - - - var apiTokenHeader = DaprClient.GetDaprApiTokenHeader(this.DaprApiToken); - var httpClient = HttpClientFactory is object ? HttpClientFactory() : new HttpClient(); - - if (this.Timeout > TimeSpan.Zero) - { - httpClient.Timeout = this.Timeout; - } + var channel = GrpcChannel.ForAddress(this.GrpcEndpoint, this.GrpcChannelOptions); + var client = new Autogenerated.Dapr.DaprClient(channel); + - return new DaprClientGrpc(channel, client, httpClient, httpEndpoint, this.JsonSerializerOptions, apiTokenHeader); + var apiTokenHeader = DaprClient.GetDaprApiTokenHeader(this.DaprApiToken); + var httpClient = HttpClientFactory is object ? HttpClientFactory() : new HttpClient(); + + if (this.Timeout > TimeSpan.Zero) + { + httpClient.Timeout = this.Timeout; } + + return new DaprClientGrpc(channel, client, httpClient, httpEndpoint, this.JsonSerializerOptions, apiTokenHeader); } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/DaprError.cs b/src/Dapr.Client/DaprError.cs index df464f6cd..05a280b19 100644 --- a/src/Dapr.Client/DaprError.cs +++ b/src/Dapr.Client/DaprError.cs @@ -11,22 +11,21 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +/// +/// The REST API operations for Dapr runtime return standard HTTP status codes. This type defines the additional +/// information returned from the Service Fabric API operations that are not successful. +/// +public class DaprError { /// - /// The REST API operations for Dapr runtime return standard HTTP status codes. This type defines the additional - /// information returned from the Service Fabric API operations that are not successful. - /// - public class DaprError - { - /// - /// Gets ErrorCode. - /// - public string ErrorCode { get; set; } + /// Gets ErrorCode. + /// + public string ErrorCode { get; set; } - /// - /// Gets error message. - /// - public string Message { get; set; } - } -} + /// + /// Gets error message. + /// + public string Message { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Client/DaprMetadata.cs b/src/Dapr.Client/DaprMetadata.cs index 4cd812e04..e9e4f5bc8 100644 --- a/src/Dapr.Client/DaprMetadata.cs +++ b/src/Dapr.Client/DaprMetadata.cs @@ -13,114 +13,113 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Represents a metadata object returned from dapr sidecar. +/// +public sealed class DaprMetadata { /// - /// Represents a metadata object returned from dapr sidecar. + /// Initializes a new instance of the class. /// - public sealed class DaprMetadata + /// The application id. + /// The registered actors metadata. + /// The list of custom attributes as key-value pairs, where key is the attribute name. + /// The loaded components metadata. + public DaprMetadata(string id, IReadOnlyList actors, IReadOnlyDictionary extended, IReadOnlyList components) { - /// - /// Initializes a new instance of the class. - /// - /// The application id. - /// The registered actors metadata. - /// The list of custom attributes as key-value pairs, where key is the attribute name. - /// The loaded components metadata. - public DaprMetadata(string id, IReadOnlyList actors, IReadOnlyDictionary extended, IReadOnlyList components) - { - Id = id; - Actors = actors; - Extended = extended; - Components = components; - } + Id = id; + Actors = actors; + Extended = extended; + Components = components; + } - /// - /// Gets the application id. - /// - public string Id { get; } + /// + /// Gets the application id. + /// + public string Id { get; } - /// - /// Gets the registered actors metadata. - /// - public IReadOnlyList Actors { get; } + /// + /// Gets the registered actors metadata. + /// + public IReadOnlyList Actors { get; } - /// - /// Gets the list of custom attributes as key-value pairs, where key is the attribute name. - /// - public IReadOnlyDictionary Extended { get; } + /// + /// Gets the list of custom attributes as key-value pairs, where key is the attribute name. + /// + public IReadOnlyDictionary Extended { get; } - /// - /// Gets the loaded components metadata. - /// - public IReadOnlyList Components { get; } - } + /// + /// Gets the loaded components metadata. + /// + public IReadOnlyList Components { get; } +} +/// +/// Represents a actor metadata object returned from dapr sidecar. +/// +public sealed class DaprActorMetadata +{ /// - /// Represents a actor metadata object returned from dapr sidecar. + /// Initializes a new instance of the class. /// - public sealed class DaprActorMetadata + /// This registered actor type. + /// The number of actors running. + public DaprActorMetadata(string type, int count) { - /// - /// Initializes a new instance of the class. - /// - /// This registered actor type. - /// The number of actors running. - public DaprActorMetadata(string type, int count) - { - Type = type; - Count = count; - } + Type = type; + Count = count; + } - /// - /// Gets the registered actor type. - /// - public string Type { get; } + /// + /// Gets the registered actor type. + /// + public string Type { get; } - /// - /// Gets the number of actors running. - /// - public int Count { get; } - } + /// + /// Gets the number of actors running. + /// + public int Count { get; } +} +/// +/// Represents a components metadata object returned from dapr sidecar. +/// +public sealed class DaprComponentsMetadata +{ /// - /// Represents a components metadata object returned from dapr sidecar. + /// Initializes a new instance of the class. /// - public sealed class DaprComponentsMetadata + /// The name of the component. + /// The component type. + /// The component version. + /// The supported capabilities for this component type and version. + public DaprComponentsMetadata(string name, string type, string version, string[] capabilities) { - /// - /// Initializes a new instance of the class. - /// - /// The name of the component. - /// The component type. - /// The component version. - /// The supported capabilities for this component type and version. - public DaprComponentsMetadata(string name, string type, string version, string[] capabilities) - { - Name = name; - Type = type; - Version = version; - Capabilities = capabilities; - } + Name = name; + Type = type; + Version = version; + Capabilities = capabilities; + } - /// - /// Gets the name of the component. - /// - public string Name { get; } + /// + /// Gets the name of the component. + /// + public string Name { get; } - /// - /// Gets the component type. - /// - public string Type { get; } + /// + /// Gets the component type. + /// + public string Type { get; } - /// - /// Gets the component version. - /// - public string Version { get; } + /// + /// Gets the component version. + /// + public string Version { get; } - /// - /// Gets the supported capabilities for this component type and version. - /// - public string[] Capabilities { get; } - } -} + /// + /// Gets the supported capabilities for this component type and version. + /// + public string[] Capabilities { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/DaprSubscribeConfigurationSource.cs b/src/Dapr.Client/DaprSubscribeConfigurationSource.cs index 084e79eee..98947767f 100644 --- a/src/Dapr.Client/DaprSubscribeConfigurationSource.cs +++ b/src/Dapr.Client/DaprSubscribeConfigurationSource.cs @@ -19,81 +19,80 @@ using Grpc.Core; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; -namespace Dapr.Client +namespace Dapr.Client; + +internal class DaprSubscribeConfigurationSource : ConfigurationSource { - internal class DaprSubscribeConfigurationSource : ConfigurationSource - { - private AsyncServerStreamingCall call; - private string id = string.Empty; + private AsyncServerStreamingCall call; + private string id = string.Empty; - /// - /// Constructor. - /// - /// The streaming call from the Dapr Subscribe Configuration API. - internal DaprSubscribeConfigurationSource(AsyncServerStreamingCall call) : base() - { - this.call = call; - } + /// + /// Constructor. + /// + /// The streaming call from the Dapr Subscribe Configuration API. + internal DaprSubscribeConfigurationSource(AsyncServerStreamingCall call) : base() + { + this.call = call; + } - /// - public override string Id => id; + /// + public override string Id => id; - public override IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) - { - return new DaprSubscribeConfigurationEnumerator(call, streamId => setIdIfNullOrEmpty(streamId), cancellationToken); - } + public override IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new DaprSubscribeConfigurationEnumerator(call, streamId => setIdIfNullOrEmpty(streamId), cancellationToken); + } - private void setIdIfNullOrEmpty(string id) + private void setIdIfNullOrEmpty(string id) + { + if (string.IsNullOrEmpty(this.id)) { - if (string.IsNullOrEmpty(this.id)) - { - this.id = id; - } + this.id = id; } } +} - internal class DaprSubscribeConfigurationEnumerator : IAsyncEnumerator> - { - private AsyncServerStreamingCall call; - private Action idCallback; - private CancellationToken cancellationToken; +internal class DaprSubscribeConfigurationEnumerator : IAsyncEnumerator> +{ + private AsyncServerStreamingCall call; + private Action idCallback; + private CancellationToken cancellationToken; - internal DaprSubscribeConfigurationEnumerator( - AsyncServerStreamingCall call, - Action idCallback, - CancellationToken cancellationToken = default) - { - this.call = call; - this.idCallback = idCallback; - this.cancellationToken = cancellationToken; - } + internal DaprSubscribeConfigurationEnumerator( + AsyncServerStreamingCall call, + Action idCallback, + CancellationToken cancellationToken = default) + { + this.call = call; + this.idCallback = idCallback; + this.cancellationToken = cancellationToken; + } - /// - public IDictionary Current + /// + public IDictionary Current + { + get { - get + var current = call.ResponseStream.Current; + if (current != null) { - var current = call.ResponseStream.Current; - if (current != null) - { - idCallback(current.Id); - return current.Items.ToDictionary(item => item.Key, item => new ConfigurationItem(item.Value.Value, item.Value.Version, item.Value.Metadata)); - } - return null; + idCallback(current.Id); + return current.Items.ToDictionary(item => item.Key, item => new ConfigurationItem(item.Value.Value, item.Value.Version, item.Value.Metadata)); } + return null; } + } - /// - public ValueTask DisposeAsync() - { - call.Dispose(); - return new ValueTask(); - } + /// + public ValueTask DisposeAsync() + { + call.Dispose(); + return new ValueTask(); + } - /// - public async ValueTask MoveNextAsync() - { - return await call.ResponseStream.MoveNext(cancellationToken); - } + /// + public async ValueTask MoveNextAsync() + { + return await call.ResponseStream.MoveNext(cancellationToken); } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/DeleteBulkStateItem.cs b/src/Dapr.Client/DeleteBulkStateItem.cs index 13a4722dc..b7eb6c0db 100644 --- a/src/Dapr.Client/DeleteBulkStateItem.cs +++ b/src/Dapr.Client/DeleteBulkStateItem.cs @@ -13,46 +13,45 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Represents a state object used for bulk delete state operation +/// +public readonly struct BulkDeleteStateItem { /// - /// Represents a state object used for bulk delete state operation + /// Initializes a new instance of the class. /// - public readonly struct BulkDeleteStateItem + /// The state key. + /// The ETag. + /// The stateOptions. + /// The metadata. + public BulkDeleteStateItem(string key, string etag, StateOptions stateOptions = default, IReadOnlyDictionary metadata = default) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The ETag. - /// The stateOptions. - /// The metadata. - public BulkDeleteStateItem(string key, string etag, StateOptions stateOptions = default, IReadOnlyDictionary metadata = default) - { - this.Key = key; - this.ETag = etag; - StateOptions = stateOptions; - Metadata = metadata; - } + this.Key = key; + this.ETag = etag; + StateOptions = stateOptions; + Metadata = metadata; + } - /// - /// Gets the state key. - /// - public string Key { get; } + /// + /// Gets the state key. + /// + public string Key { get; } - /// - /// Get the ETag. - /// - public string ETag { get; } + /// + /// Get the ETag. + /// + public string ETag { get; } - /// - /// Gets the StateOptions. - /// - public StateOptions StateOptions { get; } + /// + /// Gets the StateOptions. + /// + public StateOptions StateOptions { get; } - /// - /// Gets the Metadata. - /// - public IReadOnlyDictionary Metadata { get; } - } -} + /// + /// Gets the Metadata. + /// + public IReadOnlyDictionary Metadata { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/Extensions/EnumExtensions.cs b/src/Dapr.Client/Extensions/EnumExtensions.cs index df9c9ad33..a239583da 100644 --- a/src/Dapr.Client/Extensions/EnumExtensions.cs +++ b/src/Dapr.Client/Extensions/EnumExtensions.cs @@ -16,26 +16,25 @@ using System.Reflection; using System.Runtime.Serialization; -namespace Dapr.Client +namespace Dapr.Client; + +internal static class EnumExtensions { - internal static class EnumExtensions + /// + /// Reads the value of an enum out of the attached attribute. + /// + /// The enum. + /// The value of the enum to pull the value for. + /// + public static string GetValueFromEnumMember(this T value) where T : Enum { - /// - /// Reads the value of an enum out of the attached attribute. - /// - /// The enum. - /// The value of the enum to pull the value for. - /// - public static string GetValueFromEnumMember(this T value) where T : Enum - { - ArgumentNullException.ThrowIfNull(value, nameof(value)); + ArgumentNullException.ThrowIfNull(value, nameof(value)); - var memberInfo = typeof(T).GetMember(value.ToString(), BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); - if (memberInfo.Length <= 0) - return value.ToString(); + var memberInfo = typeof(T).GetMember(value.ToString(), BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); + if (memberInfo.Length <= 0) + return value.ToString(); - var attributes = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false); - return (attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString()) ?? value.ToString(); - } + var attributes = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false); + return (attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString()) ?? value.ToString(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/Extensions/HttpExtensions.cs b/src/Dapr.Client/Extensions/HttpExtensions.cs index 259d2747d..6e8c1a339 100644 --- a/src/Dapr.Client/Extensions/HttpExtensions.cs +++ b/src/Dapr.Client/Extensions/HttpExtensions.cs @@ -16,36 +16,35 @@ using System.Collections.Generic; using System.Text; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Provides extensions specific to HTTP types. +/// +internal static class HttpExtensions { /// - /// Provides extensions specific to HTTP types. + /// Appends key/value pairs to the query string on an HttpRequestMessage. /// - internal static class HttpExtensions + /// The uri to append the query string parameters to. + /// The key/value pairs to populate the query string with. + public static Uri AddQueryParameters(this Uri? uri, + IReadOnlyCollection>? queryStringParameters) { - /// - /// Appends key/value pairs to the query string on an HttpRequestMessage. - /// - /// The uri to append the query string parameters to. - /// The key/value pairs to populate the query string with. - public static Uri AddQueryParameters(this Uri? uri, - IReadOnlyCollection>? queryStringParameters) - { - ArgumentNullException.ThrowIfNull(uri, nameof(uri)); - if (queryStringParameters is null) - return uri; - - var uriBuilder = new UriBuilder(uri); - var qsBuilder = new StringBuilder(uriBuilder.Query); - foreach (var kvParam in queryStringParameters) - { - if (qsBuilder.Length > 0) - qsBuilder.Append('&'); - qsBuilder.Append($"{Uri.EscapeDataString(kvParam.Key)}={Uri.EscapeDataString(kvParam.Value)}"); - } + ArgumentNullException.ThrowIfNull(uri, nameof(uri)); + if (queryStringParameters is null) + return uri; - uriBuilder.Query = qsBuilder.ToString(); - return uriBuilder.Uri; + var uriBuilder = new UriBuilder(uri); + var qsBuilder = new StringBuilder(uriBuilder.Query); + foreach (var kvParam in queryStringParameters) + { + if (qsBuilder.Length > 0) + qsBuilder.Append('&'); + qsBuilder.Append($"{Uri.EscapeDataString(kvParam.Key)}={Uri.EscapeDataString(kvParam.Value)}"); } + + uriBuilder.Query = qsBuilder.ToString(); + return uriBuilder.Uri; } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/GetConfigurationResponse.cs b/src/Dapr.Client/GetConfigurationResponse.cs index 0349d134f..317a88514 100644 --- a/src/Dapr.Client/GetConfigurationResponse.cs +++ b/src/Dapr.Client/GetConfigurationResponse.cs @@ -13,33 +13,32 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Class representing the response from a GetConfiguration API call. +/// +public class GetConfigurationResponse { + private readonly IReadOnlyDictionary configMap; + /// - /// Class representing the response from a GetConfiguration API call. + /// Constructor for a GetConfigurationResponse. /// - public class GetConfigurationResponse + /// The map of keys to items that was returned in the GetConfiguration call. + public GetConfigurationResponse(IReadOnlyDictionary configMap) { - private readonly IReadOnlyDictionary configMap; - - /// - /// Constructor for a GetConfigurationResponse. - /// - /// The map of keys to items that was returned in the GetConfiguration call. - public GetConfigurationResponse(IReadOnlyDictionary configMap) - { - this.configMap = configMap; - } + this.configMap = configMap; + } - /// - /// The map of key to items returned in a GetConfiguration call. - /// - public IReadOnlyDictionary Items + /// + /// The map of key to items returned in a GetConfiguration call. + /// + public IReadOnlyDictionary Items + { + get { - get - { - return configMap; - } + return configMap; } } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/InvocationException.cs b/src/Dapr.Client/InvocationException.cs index 19183f9b1..253cd5ae2 100644 --- a/src/Dapr.Client/InvocationException.cs +++ b/src/Dapr.Client/InvocationException.cs @@ -15,53 +15,52 @@ using System.Net.Http; using Grpc.Core; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// The exception type thrown when an exception is encountered using Dapr service invocation. +/// +public class InvocationException : DaprException { /// - /// The exception type thrown when an exception is encountered using Dapr service invocation. + /// Initializes a new for a non-successful HTTP request. /// - public class InvocationException : DaprException + public InvocationException(string appId, string methodName, Exception innerException, HttpResponseMessage response) + : base(FormatExceptionForFailedRequest(appId, methodName), innerException) { - /// - /// Initializes a new for a non-successful HTTP request. - /// - public InvocationException(string appId, string methodName, Exception innerException, HttpResponseMessage response) - : base(FormatExceptionForFailedRequest(appId, methodName), innerException) - { - this.AppId = appId ?? "unknown"; - this.MethodName = methodName ?? "unknown"; - this.Response = response; - } + this.AppId = appId ?? "unknown"; + this.MethodName = methodName ?? "unknown"; + this.Response = response; + } - /// - /// Initializes a new for a non-successful gRPC request. - /// - public InvocationException(string appId, string methodName, RpcException innerException) - : base(FormatExceptionForFailedRequest(appId, methodName), innerException) - { - this.AppId = appId ?? "unknown"; - this.MethodName = methodName ?? "unknown"; - } + /// + /// Initializes a new for a non-successful gRPC request. + /// + public InvocationException(string appId, string methodName, RpcException innerException) + : base(FormatExceptionForFailedRequest(appId, methodName), innerException) + { + this.AppId = appId ?? "unknown"; + this.MethodName = methodName ?? "unknown"; + } - /// - /// Gets the destination app-id of the invocation request that failed. - /// - public string AppId { get; } + /// + /// Gets the destination app-id of the invocation request that failed. + /// + public string AppId { get; } - /// - /// Gets the destination method name of the invocation request that failed. - /// - public string MethodName { get; } + /// + /// Gets the destination method name of the invocation request that failed. + /// + public string MethodName { get; } - /// - /// Gets the of the request that failed. Will be null if the - /// failure was not related to an HTTP request or preventing the response from being recieved. - /// - public HttpResponseMessage Response { get; } + /// + /// Gets the of the request that failed. Will be null if the + /// failure was not related to an HTTP request or preventing the response from being recieved. + /// + public HttpResponseMessage Response { get; } - private static string FormatExceptionForFailedRequest(string appId, string methodName) - { - return $"An exception occurred while invoking method: '{methodName}' on app-id: '{appId}'"; - } + private static string FormatExceptionForFailedRequest(string appId, string methodName) + { + return $"An exception occurred while invoking method: '{methodName}' on app-id: '{appId}'"; } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/InvocationHandler.cs b/src/Dapr.Client/InvocationHandler.cs index 36fd6b77f..af554e3ca 100644 --- a/src/Dapr.Client/InvocationHandler.cs +++ b/src/Dapr.Client/InvocationHandler.cs @@ -19,142 +19,141 @@ #nullable enable -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// +/// A implementation that rewrites URIs of outgoing requests +/// to use the Dapr service invocation protocol. This handle allows code using +/// to use the client as-if it were communciating with the destination application directly. +/// +/// +/// The handler 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. +/// +/// +/// +/// This message handler does not distinguish between requests destined for Dapr service invocation and general +/// HTTP traffic, and will attempt to forward all traffic to the Dapr endpoint. Do not attempt to set +/// to a publicly routable URI, this will result in leaking of traffic and the Dapr +/// security token. +/// +public class InvocationHandler : DelegatingHandler { + private Uri parsedEndpoint; + private string? apiToken; + /// - /// - /// A implementation that rewrites URIs of outgoing requests - /// to use the Dapr service invocation protocol. This handle allows code using - /// to use the client as-if it were communciating with the destination application directly. - /// - /// - /// The handler 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. - /// + /// Initializes a new instance of . /// - /// - /// This message handler does not distinguish between requests destined for Dapr service invocation and general - /// HTTP traffic, and will attempt to forward all traffic to the Dapr endpoint. Do not attempt to set - /// to a publicly routable URI, this will result in leaking of traffic and the Dapr - /// security token. - /// - public class InvocationHandler : DelegatingHandler + public InvocationHandler() { - private Uri parsedEndpoint; - private string? apiToken; + this.parsedEndpoint = new Uri(DaprDefaults.GetDefaultHttpEndpoint(), UriKind.Absolute); + this.apiToken = DaprDefaults.GetDefaultDaprApiToken(null); + } - /// - /// Initializes a new instance of . - /// - public InvocationHandler() + /// + /// Gets or the sets the URI of the Dapr HTTP endpoint used for service invocation. + /// + /// The URI of the Dapr HTTP endpoint used for service invocation. + public string DaprEndpoint + { + get { - this.parsedEndpoint = new Uri(DaprDefaults.GetDefaultHttpEndpoint(), UriKind.Absolute); - this.apiToken = DaprDefaults.GetDefaultDaprApiToken(null); + return this.parsedEndpoint.OriginalString; } - - /// - /// Gets or the sets the URI of the Dapr HTTP endpoint used for service invocation. - /// - /// The URI of the Dapr HTTP endpoint used for service invocation. - public string DaprEndpoint + set { - get + if (value == null) { - return this.parsedEndpoint.OriginalString; + throw new ArgumentNullException(nameof(value)); } - set + + // This will throw a reasonable exception if the URI is invalid. + var uri = new Uri(value, UriKind.Absolute); + if (uri.Scheme != "http" && uri.Scheme != "https") { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - // This will throw a reasonable exception if the URI is invalid. - var uri = new Uri(value, UriKind.Absolute); - if (uri.Scheme != "http" && uri.Scheme != "https") - { - throw new ArgumentException("The URI scheme of the Dapr endpoint must be http or https.", "value"); - } - - this.parsedEndpoint = uri; + throw new ArgumentException("The URI scheme of the Dapr endpoint must be http or https.", "value"); } + + this.parsedEndpoint = uri; } + } - /// - /// Gets or sets the default AppId used for service invocation - /// - /// The AppId used for service invocation - public string? DefaultAppId { get; set; } + /// + /// Gets or sets the default AppId used for service invocation + /// + /// The AppId used for service invocation + public string? DefaultAppId { get; set; } - // Internal for testing - internal string? DaprApiToken + // Internal for testing + internal string? DaprApiToken + { + get => this.apiToken; + set => this.apiToken = value; + } + + /// + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var original = request.RequestUri; + if (!this.TryRewriteUri(request.RequestUri, out var rewritten)) { - get => this.apiToken; - set => this.apiToken = value; + throw new ArgumentException($"The request URI '{original}' is not a valid Dapr service invocation destination.", nameof(request)); } - /// - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + try { - var original = request.RequestUri; - if (!this.TryRewriteUri(request.RequestUri, out var rewritten)) + var apiTokenHeader = DaprClient.GetDaprApiTokenHeader(this.apiToken); + if (apiTokenHeader is not null) { - throw new ArgumentException($"The request URI '{original}' is not a valid Dapr service invocation destination.", nameof(request)); + request.Headers.Add(apiTokenHeader.Value.Key, apiTokenHeader.Value.Value); } + request.RequestUri = rewritten; - try - { - var apiTokenHeader = DaprClient.GetDaprApiTokenHeader(this.apiToken); - if (apiTokenHeader is not null) - { - request.Headers.Add(apiTokenHeader.Value.Key, apiTokenHeader.Value.Value); - } - request.RequestUri = rewritten; - - return await base.SendAsync(request, cancellationToken); - } - finally - { - request.RequestUri = original; - request.Headers.Remove("dapr-api-token"); - } + return await base.SendAsync(request, cancellationToken); } - - // Internal for testing - internal bool TryRewriteUri(Uri? uri, [NotNullWhen(true)] out Uri? rewritten) + finally { - // For now the only invalid cases are when the request URI is missing or just silly. - // We may support additional cases for validation in the future (like an allow-list of App-Ids). - if (uri is null || !uri.IsAbsoluteUri || (uri.Scheme != "http" && uri.Scheme != "https")) - { - // do nothing - rewritten = null; - return false; - } + request.RequestUri = original; + request.Headers.Remove("dapr-api-token"); + } + } - string host; + // Internal for testing + internal bool TryRewriteUri(Uri? uri, [NotNullWhen(true)] out Uri? rewritten) + { + // For now the only invalid cases are when the request URI is missing or just silly. + // We may support additional cases for validation in the future (like an allow-list of App-Ids). + if (uri is null || !uri.IsAbsoluteUri || (uri.Scheme != "http" && uri.Scheme != "https")) + { + // do nothing + rewritten = null; + return false; + } - if (this.DefaultAppId is not null && uri.Host.Equals(this.DefaultAppId, StringComparison.InvariantCultureIgnoreCase)) - { - host = this.DefaultAppId; - } - else - { - host = uri.Host; - } + string host; - var builder = new UriBuilder(uri) - { - Scheme = this.parsedEndpoint.Scheme, - Host = this.parsedEndpoint.Host, - Port = this.parsedEndpoint.Port, - Path = $"/v1.0/invoke/{host}/method" + uri.AbsolutePath, - }; - - rewritten = builder.Uri; - return true; + if (this.DefaultAppId is not null && uri.Host.Equals(this.DefaultAppId, StringComparison.InvariantCultureIgnoreCase)) + { + host = this.DefaultAppId; } + else + { + host = uri.Host; + } + + var builder = new UriBuilder(uri) + { + Scheme = this.parsedEndpoint.Scheme, + Host = this.parsedEndpoint.Host, + Port = this.parsedEndpoint.Port, + Path = $"/v1.0/invoke/{host}/method" + uri.AbsolutePath, + }; + + rewritten = builder.Uri; + return true; } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/InvocationInterceptor.cs b/src/Dapr.Client/InvocationInterceptor.cs index b4ecc4b51..434e53ae8 100644 --- a/src/Dapr.Client/InvocationInterceptor.cs +++ b/src/Dapr.Client/InvocationInterceptor.cs @@ -14,124 +14,123 @@ using Grpc.Core; using Grpc.Core.Interceptors; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// gRPC interceptor which adds the required headers for Dapr gRPC proxying. +/// +public class InvocationInterceptor : Interceptor { + private string appId; + private string daprApiToken; + /// - /// gRPC interceptor which adds the required headers for Dapr gRPC proxying. + /// Constructor. + /// The Id of the Dapr Application. + /// The api token used for authentication, can be null. /// - public class InvocationInterceptor : Interceptor + public InvocationInterceptor(string appId, string daprApiToken) { - private string appId; - private string daprApiToken; - - /// - /// Constructor. - /// The Id of the Dapr Application. - /// The api token used for authentication, can be null. - /// - public InvocationInterceptor(string appId, string daprApiToken) - { - this.appId = appId; - this.daprApiToken = daprApiToken; - } + this.appId = appId; + this.daprApiToken = daprApiToken; + } - /// - /// Intercept and add headers to a BlockingUnaryCall. - /// - /// The request to intercept. - /// The client interceptor context to add headers to. - /// The continuation of the request after all headers have been added. - public override TResponse BlockingUnaryCall( - TRequest request, - ClientInterceptorContext context, - BlockingUnaryCallContinuation continuation) - { - AddCallerMetadata(ref context); + /// + /// Intercept and add headers to a BlockingUnaryCall. + /// + /// The request to intercept. + /// The client interceptor context to add headers to. + /// The continuation of the request after all headers have been added. + public override TResponse BlockingUnaryCall( + TRequest request, + ClientInterceptorContext context, + BlockingUnaryCallContinuation continuation) + { + AddCallerMetadata(ref context); - return continuation(request, context); - } + return continuation(request, context); + } - /// - /// Intercept and add headers to a AsyncUnaryCall. - /// - /// The request to intercept. - /// The client interceptor context to add headers to. - /// The continuation of the request after all headers have been added. - public override AsyncUnaryCall AsyncUnaryCall( - TRequest request, - ClientInterceptorContext context, - AsyncUnaryCallContinuation continuation) - { - AddCallerMetadata(ref context); + /// + /// Intercept and add headers to a AsyncUnaryCall. + /// + /// The request to intercept. + /// The client interceptor context to add headers to. + /// The continuation of the request after all headers have been added. + public override AsyncUnaryCall AsyncUnaryCall( + TRequest request, + ClientInterceptorContext context, + AsyncUnaryCallContinuation continuation) + { + AddCallerMetadata(ref context); - return continuation(request, context); - } + return continuation(request, context); + } - /// - /// Intercept and add headers to a AsyncClientStreamingCall. - /// - /// The client interceptor context to add headers to. - /// The continuation of the request after all headers have been added. - public override AsyncClientStreamingCall AsyncClientStreamingCall( - ClientInterceptorContext context, - AsyncClientStreamingCallContinuation continuation) - { - AddCallerMetadata(ref context); + /// + /// Intercept and add headers to a AsyncClientStreamingCall. + /// + /// The client interceptor context to add headers to. + /// The continuation of the request after all headers have been added. + public override AsyncClientStreamingCall AsyncClientStreamingCall( + ClientInterceptorContext context, + AsyncClientStreamingCallContinuation continuation) + { + AddCallerMetadata(ref context); - return continuation(context); - } + return continuation(context); + } - /// - /// Intercept and add headers to a AsyncServerStreamingCall. - /// - /// The request to intercept. - /// The client interceptor context to add headers to. - /// The continuation of the request after all headers have been added. - public override AsyncServerStreamingCall AsyncServerStreamingCall( - TRequest request, - ClientInterceptorContext context, - AsyncServerStreamingCallContinuation continuation) - { - AddCallerMetadata(ref context); + /// + /// Intercept and add headers to a AsyncServerStreamingCall. + /// + /// The request to intercept. + /// The client interceptor context to add headers to. + /// The continuation of the request after all headers have been added. + public override AsyncServerStreamingCall AsyncServerStreamingCall( + TRequest request, + ClientInterceptorContext context, + AsyncServerStreamingCallContinuation continuation) + { + AddCallerMetadata(ref context); - return continuation(request, context); - } + return continuation(request, context); + } - /// - /// Intercept and add headers to a AsyncDuplexStreamingCall. - /// - /// The client interceptor context to add headers to. - /// The continuation of the request after all headers have been added. - public override AsyncDuplexStreamingCall AsyncDuplexStreamingCall( - ClientInterceptorContext context, - AsyncDuplexStreamingCallContinuation continuation) - { - AddCallerMetadata(ref context); + /// + /// Intercept and add headers to a AsyncDuplexStreamingCall. + /// + /// The client interceptor context to add headers to. + /// The continuation of the request after all headers have been added. + public override AsyncDuplexStreamingCall AsyncDuplexStreamingCall( + ClientInterceptorContext context, + AsyncDuplexStreamingCallContinuation continuation) + { + AddCallerMetadata(ref context); - return continuation(context); - } + return continuation(context); + } - private void AddCallerMetadata(ref ClientInterceptorContext context) - where TRequest : class - where TResponse : class + private void AddCallerMetadata(ref ClientInterceptorContext context) + where TRequest : class + where TResponse : class + { + var headers = context.Options.Headers; + + // Call doesn't have a headers collection to add to. + // Need to create a new context with headers for the call. + if (headers == null) { - var headers = context.Options.Headers; - - // Call doesn't have a headers collection to add to. - // Need to create a new context with headers for the call. - if (headers == null) - { - headers = new Metadata(); - var options = context.Options.WithHeaders(headers); - context = new ClientInterceptorContext(context.Method, context.Host, options); - } - - // Add caller metadata to call headers - headers.Add("dapr-app-id", appId); - if (daprApiToken != null) - { - headers.Add("dapr-api-token", daprApiToken); - } + headers = new Metadata(); + var options = context.Options.WithHeaders(headers); + context = new ClientInterceptorContext(context.Method, context.Host, options); } + + // Add caller metadata to call headers + headers.Add("dapr-app-id", appId); + if (daprApiToken != null) + { + headers.Add("dapr-api-token", daprApiToken); + } } } \ No newline at end of file diff --git a/src/Dapr.Client/SaveStateItem.cs b/src/Dapr.Client/SaveStateItem.cs index 818bee206..2e0da6839 100644 --- a/src/Dapr.Client/SaveStateItem.cs +++ b/src/Dapr.Client/SaveStateItem.cs @@ -13,53 +13,52 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Represents a state object used for bulk delete state operation +/// +public readonly struct SaveStateItem { /// - /// Represents a state object used for bulk delete state operation + /// Initializes a new instance of the class. /// - public readonly struct SaveStateItem + /// The state key. + /// The state value. + /// The ETag. + /// The stateOptions. + /// The metadata. + public SaveStateItem(string key, TValue value, string etag, StateOptions stateOptions = default, IReadOnlyDictionary metadata = default) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The state value. - /// The ETag. - /// The stateOptions. - /// The metadata. - public SaveStateItem(string key, TValue value, string etag, StateOptions stateOptions = default, IReadOnlyDictionary metadata = default) - { - this.Key = key; - this.Value = value; - this.ETag = etag; - this.StateOptions = stateOptions; - this.Metadata = metadata; - } + this.Key = key; + this.Value = value; + this.ETag = etag; + this.StateOptions = stateOptions; + this.Metadata = metadata; + } - /// - /// Gets the state key. - /// - public string Key { get; } + /// + /// Gets the state key. + /// + public string Key { get; } - /// - /// Gets the state value. - /// - public TValue Value { get; } + /// + /// Gets the state value. + /// + public TValue Value { get; } - /// - /// Get the ETag. - /// - public string ETag { get; } + /// + /// Get the ETag. + /// + public string ETag { get; } - /// - /// Gets the StateOptions. - /// - public StateOptions StateOptions { get; } + /// + /// Gets the StateOptions. + /// + public StateOptions StateOptions { get; } - /// - /// Gets the Metadata. - /// - public IReadOnlyDictionary Metadata { get; } - } -} + /// + /// Gets the Metadata. + /// + public IReadOnlyDictionary Metadata { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/StartWorkflowResponse.cs b/src/Dapr.Client/StartWorkflowResponse.cs index a927116cb..cc8d8ec6e 100644 --- a/src/Dapr.Client/StartWorkflowResponse.cs +++ b/src/Dapr.Client/StartWorkflowResponse.cs @@ -11,11 +11,10 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client -{ - /// - /// Initializes a new . - /// - /// The instance ID associated with this response. - public record StartWorkflowResponse(string InstanceId); -} +namespace Dapr.Client; + +/// +/// Initializes a new . +/// +/// The instance ID associated with this response. +public record StartWorkflowResponse(string InstanceId); \ No newline at end of file diff --git a/src/Dapr.Client/StateEntry.cs b/src/Dapr.Client/StateEntry.cs index 6d12d3864..723051ffb 100644 --- a/src/Dapr.Client/StateEntry.cs +++ b/src/Dapr.Client/StateEntry.cs @@ -11,136 +11,135 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Client; + +/// +/// Represents a value in the Dapr state store. +/// +/// The data type of the value. +public sealed class StateEntry { - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Client; + private readonly DaprClient client; /// - /// Represents a value in the Dapr state store. + /// Initializes a new instance of the class. /// - /// The data type of the value. - public sealed class StateEntry + /// The instance used to retrieve the value. + /// The name of the state store. + /// The state key. + /// The value. + /// The ETag. + /// + /// Application code should not need to create instances of . Use + /// to access + /// state entries. + /// + public StateEntry(DaprClient client, string storeName, string key, TValue value, string etag) { - private readonly DaprClient client; + ArgumentVerifier.ThrowIfNull(client, nameof(client)); + ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); + ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); - /// - /// Initializes a new instance of the class. - /// - /// The instance used to retrieve the value. - /// The name of the state store. - /// The state key. - /// The value. - /// The ETag. - /// - /// Application code should not need to create instances of . Use - /// to access - /// state entries. - /// - public StateEntry(DaprClient client, string storeName, string key, TValue value, string etag) - { - ArgumentVerifier.ThrowIfNull(client, nameof(client)); - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); + this.StoreName = storeName; + this.Key = key; + this.Value = value; + this.client = client; - this.StoreName = storeName; - this.Key = key; - this.Value = value; - this.client = client; - - this.ETag = etag; - } + this.ETag = etag; + } - /// - /// Gets the State Store Name. - /// - public string StoreName { get; } + /// + /// Gets the State Store Name. + /// + public string StoreName { get; } - /// - /// Gets the state key. - /// - public string Key { get; } + /// + /// Gets the state key. + /// + public string Key { get; } - /// - /// Gets or sets the value locally. This is not sent to the state store until an API (e.g. is called. - /// - public TValue Value { get; set; } + /// + /// Gets or sets the value locally. This is not sent to the state store until an API (e.g. is called. + /// + public TValue Value { get; set; } - /// - /// The ETag. - /// - public string ETag { get; } + /// + /// The ETag. + /// + public string ETag { get; } - /// - /// Deletes the entry associated with in the state store. - /// - /// A object. - /// An key/value pair that may be consumed by the state store. This depends on the state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public Task DeleteAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) - { - // ETag is intentionally not specified - return this.client.DeleteStateAsync(this.StoreName, this.Key, stateOptions, metadata, cancellationToken); - } + /// + /// Deletes the entry associated with in the state store. + /// + /// A object. + /// An key/value pair that may be consumed by the state store. This depends on the state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public Task DeleteAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + { + // ETag is intentionally not specified + return this.client.DeleteStateAsync(this.StoreName, this.Key, stateOptions, metadata, cancellationToken); + } - /// - /// Saves the current value of to the state store. - /// - /// Options for Save state operation. - /// An key/value pair that may be consumed by the state store. This is dependent on the type of state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. - public Task SaveAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) - { - // ETag is intentionally not specified - return this.client.SaveStateAsync( - storeName: this.StoreName, - key: this.Key, - value: this.Value, - metadata: metadata, - stateOptions: stateOptions, - cancellationToken: cancellationToken); - } + /// + /// Saves the current value of to the state store. + /// + /// Options for Save state operation. + /// An key/value pair that may be consumed by the state store. This is dependent on the type of state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. + public Task SaveAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + { + // ETag is intentionally not specified + return this.client.SaveStateAsync( + storeName: this.StoreName, + key: this.Key, + value: this.Value, + metadata: metadata, + stateOptions: stateOptions, + cancellationToken: cancellationToken); + } - /// - /// Tries to save the state using the etag to the Dapr state. State store implementation will allow the update only if the attached ETag matches with the latest ETag in the state store. - /// - /// An key/value pair that may be consumed by the state store. This is dependent on the type of state store used. - /// Options for Save state operation. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. - public Task TrySaveAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) - { - return this.client.TrySaveStateAsync( - this.StoreName, - this.Key, - this.Value, - this.ETag, - stateOptions, - metadata, - cancellationToken); - } + /// + /// Tries to save the state using the etag to the Dapr state. State store implementation will allow the update only if the attached ETag matches with the latest ETag in the state store. + /// + /// An key/value pair that may be consumed by the state store. This is dependent on the type of state store used. + /// Options for Save state operation. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. + public Task TrySaveAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + { + return this.client.TrySaveStateAsync( + this.StoreName, + this.Key, + this.Value, + this.ETag, + stateOptions, + metadata, + cancellationToken); + } - /// - /// Tries to delete the the state using the - /// from the Dapr state. State store implementation will allow the delete only if the attached ETag matches with the latest ETag in the state store. - /// - /// Options for Save state operation. - /// An key/value pair that may be consumed by the state store. This depends on the state store used. - /// A that can be used to cancel the operation. - /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. - public Task TryDeleteAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) - { - return this.client.TryDeleteStateAsync( - this.StoreName, - this.Key, - this.ETag, - stateOptions, - metadata, - cancellationToken); - } + /// + /// Tries to delete the the state using the + /// from the Dapr state. State store implementation will allow the delete only if the attached ETag matches with the latest ETag in the state store. + /// + /// Options for Save state operation. + /// An key/value pair that may be consumed by the state store. This depends on the state store used. + /// A that can be used to cancel the operation. + /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. + public Task TryDeleteAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + { + return this.client.TryDeleteStateAsync( + this.StoreName, + this.Key, + this.ETag, + stateOptions, + metadata, + cancellationToken); } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/StateOperationType.cs b/src/Dapr.Client/StateOperationType.cs index 396ab8afb..a91910c43 100644 --- a/src/Dapr.Client/StateOperationType.cs +++ b/src/Dapr.Client/StateOperationType.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Operation type for state operations with Dapr. +/// +public enum StateOperationType { /// - /// Operation type for state operations with Dapr. + /// Upsert a new or existing state /// - public enum StateOperationType - { - /// - /// Upsert a new or existing state - /// - Upsert, + Upsert, - /// - /// Delete a state - /// - Delete, - } -} + /// + /// Delete a state + /// + Delete, +} \ No newline at end of file diff --git a/src/Dapr.Client/StateOptions.cs b/src/Dapr.Client/StateOptions.cs index fa6f2739c..1817539bd 100644 --- a/src/Dapr.Client/StateOptions.cs +++ b/src/Dapr.Client/StateOptions.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Options when perfroming state operations with Dapr. +/// +public class StateOptions { /// - /// Options when perfroming state operations with Dapr. + /// Consistency mode for state operations with Dapr. /// - public class StateOptions - { - /// - /// Consistency mode for state operations with Dapr. - /// - public ConsistencyMode? Consistency { get; set; } + public ConsistencyMode? Consistency { get; set; } - /// - /// Concurrency mode for state operations with Dapr. - /// - public ConcurrencyMode? Concurrency { get; set; } - } -} + /// + /// Concurrency mode for state operations with Dapr. + /// + public ConcurrencyMode? Concurrency { get; set; } +} \ No newline at end of file diff --git a/src/Dapr.Client/StateQueryException.cs b/src/Dapr.Client/StateQueryException.cs index 77be58a74..6fd1dddec 100644 --- a/src/Dapr.Client/StateQueryException.cs +++ b/src/Dapr.Client/StateQueryException.cs @@ -1,34 +1,33 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Exception that is thrown when an erorr is encountered during a call to the Query API. +/// This exception contains the partial results (if any) from that exception. +/// +public class StateQueryException : DaprApiException { /// - /// Exception that is thrown when an erorr is encountered during a call to the Query API. - /// This exception contains the partial results (if any) from that exception. + /// Constructor. /// - public class StateQueryException : DaprApiException + /// The description of the exception from the source. + /// The response containing successful items, if any, in a response with errors. + /// The key(s) that encountered an error during the query. + public StateQueryException(string message, StateQueryResponse response, IReadOnlyList failedKeys) + : base(message) { - /// - /// Constructor. - /// - /// The description of the exception from the source. - /// The response containing successful items, if any, in a response with errors. - /// The key(s) that encountered an error during the query. - public StateQueryException(string message, StateQueryResponse response, IReadOnlyList failedKeys) - : base(message) - { - Response = response; - FailedKeys = failedKeys; - } + Response = response; + FailedKeys = failedKeys; + } - /// - /// The response containing successful items, if any, in a response with errors. - /// - public StateQueryResponse Response { get; } + /// + /// The response containing successful items, if any, in a response with errors. + /// + public StateQueryResponse Response { get; } - /// - /// The key(s) that encountered an error during the query. - /// - public IReadOnlyList FailedKeys { get; } - } -} + /// + /// The key(s) that encountered an error during the query. + /// + public IReadOnlyList FailedKeys { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/StateQueryResponse.cs b/src/Dapr.Client/StateQueryResponse.cs index c1cc87386..7306a668c 100644 --- a/src/Dapr.Client/StateQueryResponse.cs +++ b/src/Dapr.Client/StateQueryResponse.cs @@ -14,80 +14,79 @@ limitations under the License. #nullable enable using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Represents the response from a state query. +/// +public class StateQueryResponse { /// - /// Represents the response from a state query. + /// Constructor. /// - public class StateQueryResponse + /// The results of the query. + /// The pagination token to continue the query. + /// The metadata to be passed back to the caller. + public StateQueryResponse(IReadOnlyList> results, string token, IReadOnlyDictionary metadata) { - /// - /// Constructor. - /// - /// The results of the query. - /// The pagination token to continue the query. - /// The metadata to be passed back to the caller. - public StateQueryResponse(IReadOnlyList> results, string token, IReadOnlyDictionary metadata) - { - Results = new List>(results); - Token = token; - Metadata = metadata; - } + Results = new List>(results); + Token = token; + Metadata = metadata; + } - /// - /// The results of the query. - /// - public IReadOnlyList> Results { get; } + /// + /// The results of the query. + /// + public IReadOnlyList> Results { get; } - /// - /// The pagination token to continue the query. - /// - public string Token { get; } + /// + /// The pagination token to continue the query. + /// + public string Token { get; } - /// - /// The metadata to be passed back to the caller. - /// - public IReadOnlyDictionary Metadata { get; } - } + /// + /// The metadata to be passed back to the caller. + /// + public IReadOnlyDictionary Metadata { get; } +} +/// +/// Represents an individual item from the results of a state query. +/// +public class StateQueryItem +{ /// - /// Represents an individual item from the results of a state query. + /// Constructor. /// - public class StateQueryItem + /// The key of the returned item. + /// The value of the returned item. + /// The ETag of the returned item. + /// The error, if one occurred, of the returned item. + public StateQueryItem(string key, TValue data, string etag, string error) { - /// - /// Constructor. - /// - /// The key of the returned item. - /// The value of the returned item. - /// The ETag of the returned item. - /// The error, if one occurred, of the returned item. - public StateQueryItem(string key, TValue data, string etag, string error) - { - Key = key; - Data = data; - ETag = etag; - Error = error; - } + Key = key; + Data = data; + ETag = etag; + Error = error; + } - /// - /// The key from the matched query. - /// - public string Key { get; } + /// + /// The key from the matched query. + /// + public string Key { get; } - /// - /// The data of the key from the matched query. - /// - public TValue? Data { get; } + /// + /// The data of the key from the matched query. + /// + public TValue? Data { get; } - /// - /// The ETag for the key from the matched query. - /// - public string ETag { get; } + /// + /// The ETag for the key from the matched query. + /// + public string ETag { get; } - /// - /// The error from the query, if one occurred. - /// - public string Error { get; } - } -} + /// + /// The error from the query, if one occurred. + /// + public string Error { get; } +} \ No newline at end of file diff --git a/src/Dapr.Client/StateTransactionRequest.cs b/src/Dapr.Client/StateTransactionRequest.cs index 756f3069f..a03a2bd6b 100644 --- a/src/Dapr.Client/StateTransactionRequest.cs +++ b/src/Dapr.Client/StateTransactionRequest.cs @@ -11,66 +11,65 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +using System.Collections.Generic; + +/// +/// Represents a single request in in a StateTransaction. +/// +public sealed class StateTransactionRequest { - using System.Collections.Generic; /// - /// Represents a single request in in a StateTransaction. + /// Initializes a new instance of the class. /// - public sealed class StateTransactionRequest + /// The state key. + /// The serialized state value. + /// The operation type. + /// The etag (optional). + /// Additional key value pairs for the state (optional). + /// State options (optional). + public StateTransactionRequest(string key, byte[] value, StateOperationType operationType, string etag = default, IReadOnlyDictionary metadata = default, StateOptions options = default) { + ArgumentVerifier.ThrowIfNull(key, nameof(key)); - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The serialized state value. - /// The operation type. - /// The etag (optional). - /// Additional key value pairs for the state (optional). - /// State options (optional). - public StateTransactionRequest(string key, byte[] value, StateOperationType operationType, string etag = default, IReadOnlyDictionary metadata = default, StateOptions options = default) - { - ArgumentVerifier.ThrowIfNull(key, nameof(key)); - - this.Key = key; - this.Value = value; - this.OperationType = operationType; - this.ETag = etag; - this.Metadata = metadata; - this.Options = options; - } + this.Key = key; + this.Value = value; + this.OperationType = operationType; + this.ETag = etag; + this.Metadata = metadata; + this.Options = options; + } - /// - /// Gets the state key. - /// - public string Key { get; } + /// + /// Gets the state key. + /// + public string Key { get; } - /// - /// Gets or sets the value locally. - /// - public byte[] Value { get; set; } + /// + /// Gets or sets the value locally. + /// + public byte[] Value { get; set; } - /// - /// The Operation type. - /// - public StateOperationType OperationType { get; set; } + /// + /// The Operation type. + /// + public StateOperationType OperationType { get; set; } - /// - /// The ETag (optional). - /// - public string ETag { get; set; } + /// + /// The ETag (optional). + /// + public string ETag { get; set; } - /// - /// Additional key-value pairs to be passed to the state store (optional). - /// - public IReadOnlyDictionary Metadata { get; set; } + /// + /// Additional key-value pairs to be passed to the state store (optional). + /// + public IReadOnlyDictionary Metadata { get; set; } - /// - /// State Options (optional). - /// - public StateOptions Options; - } + /// + /// State Options (optional). + /// + public StateOptions Options; } diff --git a/src/Dapr.Client/SubscribeConfigurationResponse.cs b/src/Dapr.Client/SubscribeConfigurationResponse.cs index bea992890..06969ad57 100644 --- a/src/Dapr.Client/SubscribeConfigurationResponse.cs +++ b/src/Dapr.Client/SubscribeConfigurationResponse.cs @@ -1,38 +1,37 @@ using System.Collections.Generic; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Response for a Subscribe Configuration request. +/// +public class SubscribeConfigurationResponse { + private ConfigurationSource source; + /// - /// Response for a Subscribe Configuration request. + /// Constructor. /// - public class SubscribeConfigurationResponse + /// The that provides the actual data from the subscription. + public SubscribeConfigurationResponse(ConfigurationSource source) : base() { - private ConfigurationSource source; - - /// - /// Constructor. - /// - /// The that provides the actual data from the subscription. - public SubscribeConfigurationResponse(ConfigurationSource source) : base() - { - this.source = source; - } + this.source = source; + } - /// - /// The Id of the Subscription. This will be until the first result has been streamed. - /// After that time, the Id can be used to unsubscribe. - /// - public string Id + /// + /// The Id of the Subscription. This will be until the first result has been streamed. + /// After that time, the Id can be used to unsubscribe. + /// + public string Id + { + get { - get - { - return source.Id; - } + return source.Id; } - - /// - /// Get the that is used to read the actual subscribed configuration data. - /// - public IAsyncEnumerable> Source => source; } -} + + /// + /// Get the that is used to read the actual subscribed configuration data. + /// + public IAsyncEnumerable> Source => source; +} \ No newline at end of file diff --git a/src/Dapr.Client/TryLockResponse.cs b/src/Dapr.Client/TryLockResponse.cs index e9057fbb4..251777d92 100644 --- a/src/Dapr.Client/TryLockResponse.cs +++ b/src/Dapr.Client/TryLockResponse.cs @@ -14,48 +14,47 @@ using System; using System.Threading.Tasks; -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Class representing the response from a Lock API call. +/// +[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] +public sealed class TryLockResponse : IAsyncDisposable { /// - /// Class representing the response from a Lock API call. + /// The success value of the tryLock API call + /// + public bool Success { get; init; } + + /// + /// The resourceId required to unlock the lock /// - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public sealed class TryLockResponse : IAsyncDisposable + public string ResourceId { get; init; } + + /// + /// The LockOwner required to unlock the lock + /// + public string LockOwner { get; init; } + + /// + /// The StoreName required to unlock the lock + /// + public string StoreName { get; init; } + + /// + /// Constructor for a TryLockResponse. + /// + public TryLockResponse() { - /// - /// The success value of the tryLock API call - /// - public bool Success { get; init; } - - /// - /// The resourceId required to unlock the lock - /// - public string ResourceId { get; init; } - - /// - /// The LockOwner required to unlock the lock - /// - public string LockOwner { get; init; } - - /// - /// The StoreName required to unlock the lock - /// - public string StoreName { get; init; } - - /// - /// Constructor for a TryLockResponse. - /// - public TryLockResponse() - { - } + } - /// - public async ValueTask DisposeAsync() { - using (var client = new DaprClientBuilder().Build()) { - if(this.Success) { - await client.Unlock(StoreName, ResourceId, LockOwner); - } + /// + public async ValueTask DisposeAsync() { + using (var client = new DaprClientBuilder().Build()) { + if(this.Success) { + await client.Unlock(StoreName, ResourceId, LockOwner); } } } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/TypeConverters.cs b/src/Dapr.Client/TypeConverters.cs index 13ab6920c..e1cb4e26d 100644 --- a/src/Dapr.Client/TypeConverters.cs +++ b/src/Dapr.Client/TypeConverters.cs @@ -11,54 +11,53 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client -{ - using System.Text.Json; - using Google.Protobuf; - using Google.Protobuf.WellKnownTypes; +namespace Dapr.Client; + +using System.Text.Json; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +/// +/// Some type converters. +/// +internal static class TypeConverters +{ /// - /// Some type converters. + /// Converts an arbitrary type to a based . /// - internal static class TypeConverters + /// The data to convert. + /// The JSON serialization options. + /// The type of the given data. + /// The given data as JSON based byte string. + public static ByteString ToJsonByteString(T data, JsonSerializerOptions options) { - /// - /// Converts an arbitrary type to a based . - /// - /// The data to convert. - /// The JSON serialization options. - /// The type of the given data. - /// The given data as JSON based byte string. - public static ByteString ToJsonByteString(T data, JsonSerializerOptions options) - { - var bytes = JsonSerializer.SerializeToUtf8Bytes(data, options); - return ByteString.CopyFrom(bytes); - } + var bytes = JsonSerializer.SerializeToUtf8Bytes(data, options); + return ByteString.CopyFrom(bytes); + } - public static Any ToJsonAny(T data, JsonSerializerOptions options) - { - return new Any() - { - Value = ToJsonByteString(data, options), + public static Any ToJsonAny(T data, JsonSerializerOptions options) + { + return new Any() + { + Value = ToJsonByteString(data, options), - // This isn't really compliant protobuf, because we're not setting TypeUrl, but it's - // what Dapr understands. - }; - } + // This isn't really compliant protobuf, because we're not setting TypeUrl, but it's + // what Dapr understands. + }; + } - public static T FromJsonByteString(ByteString bytes, JsonSerializerOptions options) + public static T FromJsonByteString(ByteString bytes, JsonSerializerOptions options) + { + if (bytes.Length == 0) { - if (bytes.Length == 0) - { - return default; - } - - return JsonSerializer.Deserialize(bytes.Span, options); + return default; } + + return JsonSerializer.Deserialize(bytes.Span, options); + } - public static T FromJsonAny(Any any, JsonSerializerOptions options) - { - return FromJsonByteString(any.Value, options); - } + public static T FromJsonAny(Any any, JsonSerializerOptions options) + { + return FromJsonByteString(any.Value, options); } -} +} \ No newline at end of file diff --git a/src/Dapr.Client/UnlockResponse.cs b/src/Dapr.Client/UnlockResponse.cs index 68cc40670..09bf31a60 100644 --- a/src/Dapr.Client/UnlockResponse.cs +++ b/src/Dapr.Client/UnlockResponse.cs @@ -11,47 +11,46 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Enum representing the response from a Unlock API call. +/// +public enum LockStatus { /// - /// Enum representing the response from a Unlock API call. + /// Succes stating the lock is released. /// - public enum LockStatus - { - /// - /// Succes stating the lock is released. - /// - Success, - /// - /// LockDoesNotExist stating the lock does not exist. - /// - LockDoesNotExist, - /// - /// LockBelongsToOthers stating the lock is acquired by a different process. - /// - LockBelongsToOthers, - /// - /// InternalError statign an error in unlocking. - /// - InternalError, - } + Success, + /// + /// LockDoesNotExist stating the lock does not exist. + /// + LockDoesNotExist, + /// + /// LockBelongsToOthers stating the lock is acquired by a different process. + /// + LockBelongsToOthers, + /// + /// InternalError statign an error in unlocking. + /// + InternalError, +} +/// +/// Class representing the response from a Unlock API call. +/// +public class UnlockResponse +{ /// - /// Class representing the response from a Unlock API call. + /// The status of unlock API call /// - public class UnlockResponse - { - /// - /// The status of unlock API call - /// - public LockStatus status { set; get; } + public LockStatus status { set; get; } - /// - /// Constructor for a UnlockResponse. - /// - /// The status value that is returned in the UnLock call. - public UnlockResponse(LockStatus status) { - this.status = status; - } + /// + /// Constructor for a UnlockResponse. + /// + /// The status value that is returned in the UnLock call. + public UnlockResponse(LockStatus status) { + this.status = status; } } \ No newline at end of file diff --git a/src/Dapr.Client/UnsubscribeConfigurationResponse.cs b/src/Dapr.Client/UnsubscribeConfigurationResponse.cs index 8208efb39..112578067 100644 --- a/src/Dapr.Client/UnsubscribeConfigurationResponse.cs +++ b/src/Dapr.Client/UnsubscribeConfigurationResponse.cs @@ -1,29 +1,28 @@ -namespace Dapr.Client +namespace Dapr.Client; + +/// +/// Response from an Unsubscribe Configuration call. +/// +public class UnsubscribeConfigurationResponse { /// - /// Response from an Unsubscribe Configuration call. + /// Boolean representing if the request was successful or not. /// - public class UnsubscribeConfigurationResponse - { - /// - /// Boolean representing if the request was successful or not. - /// - public bool Ok { get; } + public bool Ok { get; } - /// - /// The message from the Configuration API. - /// - public string Message { get; } + /// + /// The message from the Configuration API. + /// + public string Message { get; } - /// - /// Constructor. - /// - /// Boolean indicating success. - /// Message from the Configuration API. - public UnsubscribeConfigurationResponse(bool ok, string message) - { - Ok = ok; - Message = message; - } + /// + /// Constructor. + /// + /// Boolean indicating success. + /// Message from the Configuration API. + public UnsubscribeConfigurationResponse(bool ok, string message) + { + Ok = ok; + Message = message; } -} +} \ No newline at end of file diff --git a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreExtension.cs b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreExtension.cs index 190acacd1..f0910ad0b 100644 --- a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreExtension.cs +++ b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreExtension.cs @@ -17,84 +17,83 @@ using Dapr.Client; using Microsoft.Extensions.Configuration; -namespace Dapr.Extensions.Configuration +namespace Dapr.Extensions.Configuration; + +/// +/// Extension used to call the Dapr Configuration API and store the values in a . +/// +public static class DaprConfigurationStoreExtension { /// - /// Extension used to call the Dapr Configuration API and store the values in a . + /// Register a constant configuration store. This will make one call to Dapr with the given keys to the + /// Configuration API. /// - public static class DaprConfigurationStoreExtension + /// The to add to. + /// The configuration store to query. + /// The keys, if any, to request. If empty, returns all configuration items. + /// The used for the request. + /// The used to configure the timeout waiting for Dapr. + /// Optional metadata sent to the configuration store. + /// The . + public static IConfigurationBuilder AddDaprConfigurationStore( + this IConfigurationBuilder configurationBuilder, + string store, + IReadOnlyList keys, + DaprClient client, + TimeSpan sidecarWaitTimeout, + IReadOnlyDictionary? metadata = default) { - /// - /// Register a constant configuration store. This will make one call to Dapr with the given keys to the - /// Configuration API. - /// - /// The to add to. - /// The configuration store to query. - /// The keys, if any, to request. If empty, returns all configuration items. - /// The used for the request. - /// The used to configure the timeout waiting for Dapr. - /// Optional metadata sent to the configuration store. - /// The . - public static IConfigurationBuilder AddDaprConfigurationStore( - this IConfigurationBuilder configurationBuilder, - string store, - IReadOnlyList keys, - DaprClient client, - TimeSpan sidecarWaitTimeout, - IReadOnlyDictionary? metadata = default) + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(keys, nameof(keys)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); + + configurationBuilder.Add(new DaprConfigurationStoreSource() { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(keys, nameof(keys)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); + Store = store, + Keys = keys, + Client = client, + SidecarWaitTimeout = sidecarWaitTimeout, + IsStreaming = false, + Metadata = metadata + }); - configurationBuilder.Add(new DaprConfigurationStoreSource() - { - Store = store, - Keys = keys, - Client = client, - SidecarWaitTimeout = sidecarWaitTimeout, - IsStreaming = false, - Metadata = metadata - }); + return configurationBuilder; + } - return configurationBuilder; - } + /// + /// Register a streaming configuration store. This opens a stream to the Dapr Configuration API which + /// will get updates to keys should any occur. This stream will not be closed until canceled with the + /// or the configuration is unsubscribed in Dapr. + /// + /// The to add to. + /// The configuration store to query. + /// The keys, if any, to request. If empty, returns all configuration items. + /// The used for the request. + /// The used to configure the timeout waiting for Dapr. + /// Optional metadata sent to the configuration store. + /// The . + public static IConfigurationBuilder AddStreamingDaprConfigurationStore( + this IConfigurationBuilder configurationBuilder, + string store, + IReadOnlyList keys, + DaprClient client, + TimeSpan sidecarWaitTimeout, + IReadOnlyDictionary? metadata = default) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(keys, nameof(keys)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); - /// - /// Register a streaming configuration store. This opens a stream to the Dapr Configuration API which - /// will get updates to keys should any occur. This stream will not be closed until canceled with the - /// or the configuration is unsubscribed in Dapr. - /// - /// The to add to. - /// The configuration store to query. - /// The keys, if any, to request. If empty, returns all configuration items. - /// The used for the request. - /// The used to configure the timeout waiting for Dapr. - /// Optional metadata sent to the configuration store. - /// The . - public static IConfigurationBuilder AddStreamingDaprConfigurationStore( - this IConfigurationBuilder configurationBuilder, - string store, - IReadOnlyList keys, - DaprClient client, - TimeSpan sidecarWaitTimeout, - IReadOnlyDictionary? metadata = default) + configurationBuilder.Add(new DaprConfigurationStoreSource() { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(keys, nameof(keys)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); - - configurationBuilder.Add(new DaprConfigurationStoreSource() - { - Store = store, - Keys = keys, - Client = client, - SidecarWaitTimeout = sidecarWaitTimeout, - IsStreaming = true, - Metadata = metadata - }); + Store = store, + Keys = keys, + Client = client, + SidecarWaitTimeout = sidecarWaitTimeout, + IsStreaming = true, + Metadata = metadata + }); - return configurationBuilder; - } + return configurationBuilder; } -} +} \ No newline at end of file diff --git a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreProvider.cs b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreProvider.cs index 461ee9959..b5f2900d8 100644 --- a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreProvider.cs +++ b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreProvider.cs @@ -18,108 +18,107 @@ using Dapr.Client; using Microsoft.Extensions.Configuration; -namespace Dapr.Extensions.Configuration +namespace Dapr.Extensions.Configuration; + +/// +/// A configuration provider that utilizes the Dapr Configuration API. It can either be a single, constant +/// call or a streaming call. +/// +internal class DaprConfigurationStoreProvider : ConfigurationProvider, IDisposable { + private string store; + private IReadOnlyList keys; + private DaprClient daprClient; + private TimeSpan sidecarWaitTimeout; + private bool isStreaming; + private IReadOnlyDictionary? metadata; + private CancellationTokenSource cts; + private Task subscribeTask = Task.CompletedTask; + /// - /// A configuration provider that utilizes the Dapr Configuration API. It can either be a single, constant - /// call or a streaming call. + /// Constructor. /// - internal class DaprConfigurationStoreProvider : ConfigurationProvider, IDisposable + /// The configuration store to query. + /// The keys, if any, to request. If empty, returns all configuration items. + /// The used for the request. + /// The used to configure the timeout waiting for Dapr. + /// Determines if the source is streaming or not. + /// Optional metadata sent to the configuration store. + public DaprConfigurationStoreProvider( + string store, + IReadOnlyList keys, + DaprClient daprClient, + TimeSpan sidecarWaitTimeout, + bool isStreaming = false, + IReadOnlyDictionary? metadata = default) { - private string store; - private IReadOnlyList keys; - private DaprClient daprClient; - private TimeSpan sidecarWaitTimeout; - private bool isStreaming; - private IReadOnlyDictionary? metadata; - private CancellationTokenSource cts; - private Task subscribeTask = Task.CompletedTask; + this.store = store; + this.keys = keys; + this.daprClient = daprClient; + this.sidecarWaitTimeout = sidecarWaitTimeout; + this.isStreaming = isStreaming; + this.metadata = metadata ?? new Dictionary(); + this.cts = new CancellationTokenSource(); + } - /// - /// Constructor. - /// - /// The configuration store to query. - /// The keys, if any, to request. If empty, returns all configuration items. - /// The used for the request. - /// The used to configure the timeout waiting for Dapr. - /// Determines if the source is streaming or not. - /// Optional metadata sent to the configuration store. - public DaprConfigurationStoreProvider( - string store, - IReadOnlyList keys, - DaprClient daprClient, - TimeSpan sidecarWaitTimeout, - bool isStreaming = false, - IReadOnlyDictionary? metadata = default) - { - this.store = store; - this.keys = keys; - this.daprClient = daprClient; - this.sidecarWaitTimeout = sidecarWaitTimeout; - this.isStreaming = isStreaming; - this.metadata = metadata ?? new Dictionary(); - this.cts = new CancellationTokenSource(); - } + public void Dispose() + { + cts.Cancel(); + } + + /// + public override void Load() => LoadAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - public void Dispose() + private async Task LoadAsync() + { + // Wait for the sidecar to become available. + using (var tokenSource = new CancellationTokenSource(sidecarWaitTimeout)) { - cts.Cancel(); + await daprClient.WaitForSidecarAsync(tokenSource.Token); } - /// - public override void Load() => LoadAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - - private async Task LoadAsync() + if (isStreaming) { - // Wait for the sidecar to become available. - using (var tokenSource = new CancellationTokenSource(sidecarWaitTimeout)) + subscribeTask = Task.Run(async () => { - await daprClient.WaitForSidecarAsync(tokenSource.Token); - } - - if (isStreaming) - { - subscribeTask = Task.Run(async () => + while (!cts.Token.IsCancellationRequested) { - while (!cts.Token.IsCancellationRequested) + var id = string.Empty; + try { - var id = string.Empty; - try + var subscribeConfigurationResponse = await daprClient.SubscribeConfiguration(store, keys, metadata, cts.Token); + await foreach (var items in subscribeConfigurationResponse.Source.WithCancellation(cts.Token)) { - var subscribeConfigurationResponse = await daprClient.SubscribeConfiguration(store, keys, metadata, cts.Token); - await foreach (var items in subscribeConfigurationResponse.Source.WithCancellation(cts.Token)) + var data = new Dictionary(Data, StringComparer.OrdinalIgnoreCase); + foreach (var item in items) { - var data = new Dictionary(Data, StringComparer.OrdinalIgnoreCase); - foreach (var item in items) - { - id = subscribeConfigurationResponse.Id; - data[item.Key] = item.Value.Value; - } - Data = data; - // Whenever we get an update, make sure to update the reloadToken. - OnReload(); + id = subscribeConfigurationResponse.Id; + data[item.Key] = item.Value.Value; } + Data = data; + // Whenever we get an update, make sure to update the reloadToken. + OnReload(); } - catch (Exception) + } + catch (Exception) + { + // If we catch an exception, try and cancel the subscription so we can connect again. + if (!string.IsNullOrEmpty(id)) { - // If we catch an exception, try and cancel the subscription so we can connect again. - if (!string.IsNullOrEmpty(id)) - { - await daprClient.UnsubscribeConfiguration(store, id); - } + await daprClient.UnsubscribeConfiguration(store, id); } } - }); - } - else - { - // We don't need to worry about ReloadTokens here because it is a constant response. - var getConfigurationResponse = await daprClient.GetConfiguration(store, keys, metadata, cts.Token); - foreach (var item in getConfigurationResponse.Items) - { - Set(item.Key, item.Value.Value); } + }); + } + else + { + // We don't need to worry about ReloadTokens here because it is a constant response. + var getConfigurationResponse = await daprClient.GetConfiguration(store, keys, metadata, cts.Token); + foreach (var item in getConfigurationResponse.Items) + { + Set(item.Key, item.Value.Value); } } } -} +} \ No newline at end of file diff --git a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs index e7c2bf34a..513e156b0 100644 --- a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs +++ b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs @@ -16,47 +16,46 @@ using Dapr.Client; using Microsoft.Extensions.Configuration; -namespace Dapr.Extensions.Configuration +namespace Dapr.Extensions.Configuration; + +/// +/// Configuration source that provides a . +/// +public class DaprConfigurationStoreSource : IConfigurationSource { /// - /// Configuration source that provides a . + /// The Configuration Store to query. /// - public class DaprConfigurationStoreSource : IConfigurationSource + public string Store { get; set; } = default!; + + /// + /// The list of keys to request from the configuration. If empty, request all keys. + /// + public IReadOnlyList Keys { get; set; } = default!; + + /// + /// The used to query the configuration. + /// + public DaprClient Client { get; set; } = default!; + + /// + /// Boolean stating if this is a streaming call or not. + /// + public bool IsStreaming { get; set; } = false; + + /// + /// Gets or sets the that is used to control the timeout waiting for the Dapr sidecar to become healthly. + /// + public TimeSpan SidecarWaitTimeout { get; set; } + + /// + /// The optional metadata to be sent to the configuration store. + /// + public IReadOnlyDictionary? Metadata { get; set; } = default; + + /// + public IConfigurationProvider Build(IConfigurationBuilder builder) { - /// - /// The Configuration Store to query. - /// - public string Store { get; set; } = default!; - - /// - /// The list of keys to request from the configuration. If empty, request all keys. - /// - public IReadOnlyList Keys { get; set; } = default!; - - /// - /// The used to query the configuration. - /// - public DaprClient Client { get; set; } = default!; - - /// - /// Boolean stating if this is a streaming call or not. - /// - public bool IsStreaming { get; set; } = false; - - /// - /// Gets or sets the that is used to control the timeout waiting for the Dapr sidecar to become healthly. - /// - public TimeSpan SidecarWaitTimeout { get; set; } - - /// - /// The optional metadata to be sent to the configuration store. - /// - public IReadOnlyDictionary? Metadata { get; set; } = default; - - /// - public IConfigurationProvider Build(IConfigurationBuilder builder) - { - return new DaprConfigurationStoreProvider(Store, Keys, Client, SidecarWaitTimeout, IsStreaming, Metadata); - } + return new DaprConfigurationStoreProvider(Store, Keys, Client, SidecarWaitTimeout, IsStreaming, Metadata); } -} +} \ No newline at end of file diff --git a/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs b/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs index 6d86dc046..493c40bba 100644 --- a/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs +++ b/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs @@ -13,60 +13,59 @@ using System.Collections.Generic; -namespace Dapr.Extensions.Configuration +namespace Dapr.Extensions.Configuration; + +/// +/// Represents the name and metadata for a Secret. +/// +public class DaprSecretDescriptor { /// - /// Represents the name and metadata for a Secret. + /// The name of the secret to retrieve from the Dapr secret store. /// - public class DaprSecretDescriptor - { - /// - /// The name of the secret to retrieve from the Dapr secret store. - /// - /// - /// If the is not specified, this value will also be used as the key to retrieve the secret from the associated source secret store. - /// - public string SecretName { get; } + /// + /// If the is not specified, this value will also be used as the key to retrieve the secret from the associated source secret store. + /// + public string SecretName { get; } - /// - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// - public IReadOnlyDictionary Metadata { get; } + /// + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// + public IReadOnlyDictionary Metadata { get; } - /// - /// A value indicating whether to throw an exception if the secret is not found in the source secret store. - /// - /// - /// Setting this value to will suppress the exception; otherwise, will not. - /// - public bool IsRequired { get; } + /// + /// A value indicating whether to throw an exception if the secret is not found in the source secret store. + /// + /// + /// Setting this value to will suppress the exception; otherwise, will not. + /// + public bool IsRequired { get; } - /// - /// The secret key that maps to the to retrieve from the source secret store. - /// - /// - /// Use this property when the does not match the key used to retrieve the secret from the source secret store. - /// - public string SecretKey { get; } + /// + /// The secret key that maps to the to retrieve from the source secret store. + /// + /// + /// Use this property when the does not match the key used to retrieve the secret from the source secret store. + /// + public string SecretKey { get; } - /// - /// Secret Descriptor Constructor - /// - public DaprSecretDescriptor(string secretName, bool isRequired = true, string secretKey = "") - : this(secretName, new Dictionary(), isRequired, secretKey) - { + /// + /// Secret Descriptor Constructor + /// + public DaprSecretDescriptor(string secretName, bool isRequired = true, string secretKey = "") + : this(secretName, new Dictionary(), isRequired, secretKey) + { - } + } - /// - /// Secret Descriptor Constructor - /// - public DaprSecretDescriptor(string secretName, IReadOnlyDictionary metadata, bool isRequired = true, string secretKey = "") - { - SecretName = secretName; - Metadata = metadata; - IsRequired = isRequired; - SecretKey = string.IsNullOrEmpty(secretKey) ? secretName : secretKey; - } + /// + /// Secret Descriptor Constructor + /// + public DaprSecretDescriptor(string secretName, IReadOnlyDictionary metadata, bool isRequired = true, string secretKey = "") + { + SecretName = secretName; + Metadata = metadata; + IsRequired = isRequired; + SecretKey = string.IsNullOrEmpty(secretKey) ? secretName : secretKey; } -} +} \ No newline at end of file diff --git a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs index 15ae6ab80..7df531e3e 100644 --- a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs +++ b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs @@ -18,205 +18,204 @@ using Dapr.Extensions.Configuration.DaprSecretStore; using System.Linq; -namespace Dapr.Extensions.Configuration +namespace Dapr.Extensions.Configuration; + +/// +/// Extension methods for registering with . +/// +public static class DaprSecretStoreConfigurationExtensions { /// - /// Extension methods for registering with . + /// Adds an that reads configuration values from the Dapr Secret Store. /// - public static class DaprSecretStoreConfigurationExtensions + /// The to add to. + /// Dapr secret store name. + /// The secrets to retrieve. + /// The Dapr client + /// The . + public static IConfigurationBuilder AddDaprSecretStore( + this IConfigurationBuilder configurationBuilder, + string store, + IEnumerable secretDescriptors, + DaprClient client) { - /// - /// Adds an that reads configuration values from the Dapr Secret Store. - /// - /// The to add to. - /// Dapr secret store name. - /// The secrets to retrieve. - /// The Dapr client - /// The . - public static IConfigurationBuilder AddDaprSecretStore( - this IConfigurationBuilder configurationBuilder, - string store, - IEnumerable secretDescriptors, - DaprClient client) - { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); - - configurationBuilder.Add(new DaprSecretStoreConfigurationSource() - { - Store = store, - SecretDescriptors = secretDescriptors, - Client = client - }); - - return configurationBuilder; - } + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); - /// - /// Adds an that reads configuration values from the Dapr Secret Store. - /// - /// The to add to. - /// Dapr secret store name. - /// The secrets to retrieve. - /// The Dapr client. - /// The used to configure the timeout waiting for Dapr. - /// The . - public static IConfigurationBuilder AddDaprSecretStore( - this IConfigurationBuilder configurationBuilder, - string store, - IEnumerable secretDescriptors, - DaprClient client, - TimeSpan sidecarWaitTimeout) + configurationBuilder.Add(new DaprSecretStoreConfigurationSource() { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); - - configurationBuilder.Add(new DaprSecretStoreConfigurationSource() - { - Store = store, - SecretDescriptors = secretDescriptors, - Client = client, - SidecarWaitTimeout = sidecarWaitTimeout - }); - - return configurationBuilder; - } + Store = store, + SecretDescriptors = secretDescriptors, + Client = client + }); + + return configurationBuilder; + } + + /// + /// Adds an that reads configuration values from the Dapr Secret Store. + /// + /// The to add to. + /// Dapr secret store name. + /// The secrets to retrieve. + /// The Dapr client. + /// The used to configure the timeout waiting for Dapr. + /// The . + public static IConfigurationBuilder AddDaprSecretStore( + this IConfigurationBuilder configurationBuilder, + string store, + IEnumerable secretDescriptors, + DaprClient client, + TimeSpan sidecarWaitTimeout) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); - /// - /// Adds an that reads configuration values from the Dapr Secret Store. - /// - /// The to add to. - /// Dapr secret store name. - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// The Dapr client - /// The . - public static IConfigurationBuilder AddDaprSecretStore( - this IConfigurationBuilder configurationBuilder, - string store, - DaprClient client, - IReadOnlyDictionary? metadata = null) + configurationBuilder.Add(new DaprSecretStoreConfigurationSource() { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); + Store = store, + SecretDescriptors = secretDescriptors, + Client = client, + SidecarWaitTimeout = sidecarWaitTimeout + }); - configurationBuilder.Add(new DaprSecretStoreConfigurationSource() - { - Store = store, - Metadata = metadata, - Client = client - }); + return configurationBuilder; + } - return configurationBuilder; - } + /// + /// Adds an that reads configuration values from the Dapr Secret Store. + /// + /// The to add to. + /// Dapr secret store name. + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// The Dapr client + /// The . + public static IConfigurationBuilder AddDaprSecretStore( + this IConfigurationBuilder configurationBuilder, + string store, + DaprClient client, + IReadOnlyDictionary? metadata = null) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); - /// - /// Adds an that reads configuration values from the Dapr Secret Store. - /// - /// The to add to. - /// Dapr secret store name. - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// The Dapr client - /// The used to configure the timeout waiting for Dapr. - /// The . - public static IConfigurationBuilder AddDaprSecretStore( - this IConfigurationBuilder configurationBuilder, - string store, - DaprClient client, - TimeSpan sidecarWaitTimeout, - IReadOnlyDictionary? metadata = null) + configurationBuilder.Add(new DaprSecretStoreConfigurationSource() { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); - - configurationBuilder.Add(new DaprSecretStoreConfigurationSource() - { - Store = store, - Metadata = metadata, - Client = client, - SidecarWaitTimeout = sidecarWaitTimeout - }); - - return configurationBuilder; - } + Store = store, + Metadata = metadata, + Client = client + }); + + return configurationBuilder; + } - /// - /// Adds an that reads configuration values from the Dapr Secret Store. - /// - /// The to add to. - /// Dapr secret store name. - /// A collection of delimiters that will be replaced by ':' in the key of every secret. - /// The Dapr client - /// The . - public static IConfigurationBuilder AddDaprSecretStore( - this IConfigurationBuilder configurationBuilder, - string store, - DaprClient client, - IEnumerable? keyDelimiters) + /// + /// Adds an that reads configuration values from the Dapr Secret Store. + /// + /// The to add to. + /// Dapr secret store name. + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// The Dapr client + /// The used to configure the timeout waiting for Dapr. + /// The . + public static IConfigurationBuilder AddDaprSecretStore( + this IConfigurationBuilder configurationBuilder, + string store, + DaprClient client, + TimeSpan sidecarWaitTimeout, + IReadOnlyDictionary? metadata = null) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); + + configurationBuilder.Add(new DaprSecretStoreConfigurationSource() { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); + Store = store, + Metadata = metadata, + Client = client, + SidecarWaitTimeout = sidecarWaitTimeout + }); - var source = new DaprSecretStoreConfigurationSource - { - Store = store, - Client = client - }; + return configurationBuilder; + } - if (keyDelimiters != null) - { - source.KeyDelimiters = keyDelimiters.ToList(); - } + /// + /// Adds an that reads configuration values from the Dapr Secret Store. + /// + /// The to add to. + /// Dapr secret store name. + /// A collection of delimiters that will be replaced by ':' in the key of every secret. + /// The Dapr client + /// The . + public static IConfigurationBuilder AddDaprSecretStore( + this IConfigurationBuilder configurationBuilder, + string store, + DaprClient client, + IEnumerable? keyDelimiters) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); - configurationBuilder.Add(source); + var source = new DaprSecretStoreConfigurationSource + { + Store = store, + Client = client + }; - return configurationBuilder; + if (keyDelimiters != null) + { + source.KeyDelimiters = keyDelimiters.ToList(); } - /// - /// Adds an that reads configuration values from the Dapr Secret Store. - /// - /// The to add to. - /// Dapr secret store name. - /// A collection of delimiters that will be replaced by ':' in the key of every secret. - /// The Dapr client - /// The used to configure the timeout waiting for Dapr. - /// The . - public static IConfigurationBuilder AddDaprSecretStore( - this IConfigurationBuilder configurationBuilder, - string store, - DaprClient client, - IEnumerable? keyDelimiters, - TimeSpan sidecarWaitTimeout) - { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); + configurationBuilder.Add(source); - var source = new DaprSecretStoreConfigurationSource - { - Store = store, - Client = client, - SidecarWaitTimeout = sidecarWaitTimeout - }; + return configurationBuilder; + } - if (keyDelimiters != null) - { - source.KeyDelimiters = keyDelimiters.ToList(); - } + /// + /// Adds an that reads configuration values from the Dapr Secret Store. + /// + /// The to add to. + /// Dapr secret store name. + /// A collection of delimiters that will be replaced by ':' in the key of every secret. + /// The Dapr client + /// The used to configure the timeout waiting for Dapr. + /// The . + public static IConfigurationBuilder AddDaprSecretStore( + this IConfigurationBuilder configurationBuilder, + string store, + DaprClient client, + IEnumerable? keyDelimiters, + TimeSpan sidecarWaitTimeout) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); - configurationBuilder.Add(source); + var source = new DaprSecretStoreConfigurationSource + { + Store = store, + Client = client, + SidecarWaitTimeout = sidecarWaitTimeout + }; - return configurationBuilder; + if (keyDelimiters != null) + { + source.KeyDelimiters = keyDelimiters.ToList(); } - /// - /// Adds an that reads configuration values from the command line. - /// - /// The to add to. - /// Configures the source. - /// The . - public static IConfigurationBuilder AddDaprSecretStore(this IConfigurationBuilder configurationBuilder, Action configureSource) - => configurationBuilder.Add(configureSource); + configurationBuilder.Add(source); + + return configurationBuilder; } -} + + /// + /// Adds an that reads configuration values from the command line. + /// + /// The to add to. + /// Configures the source. + /// The . + public static IConfigurationBuilder AddDaprSecretStore(this IConfigurationBuilder configurationBuilder, Action configureSource) + => configurationBuilder.Add(configureSource); +} \ No newline at end of file diff --git a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs index ecd0ac91b..b2d4dc417 100644 --- a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs +++ b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs @@ -19,245 +19,244 @@ using Dapr.Client; using Microsoft.Extensions.Configuration; -namespace Dapr.Extensions.Configuration.DaprSecretStore -{ - /// - /// A Dapr Secret Store based . - /// - internal class DaprSecretStoreConfigurationProvider : ConfigurationProvider - { - internal static readonly TimeSpan DefaultSidecarWaitTimeout = TimeSpan.FromSeconds(5); - - private readonly string store; +namespace Dapr.Extensions.Configuration.DaprSecretStore; - private readonly bool normalizeKey; +/// +/// A Dapr Secret Store based . +/// +internal class DaprSecretStoreConfigurationProvider : ConfigurationProvider +{ + internal static readonly TimeSpan DefaultSidecarWaitTimeout = TimeSpan.FromSeconds(5); - private readonly IList? keyDelimiters; + private readonly string store; - private readonly IEnumerable? secretDescriptors; + private readonly bool normalizeKey; - private readonly IReadOnlyDictionary? metadata; + private readonly IList? keyDelimiters; - private readonly DaprClient client; + private readonly IEnumerable? secretDescriptors; - private readonly TimeSpan sidecarWaitTimeout; + private readonly IReadOnlyDictionary? metadata; - /// - /// Creates a new instance of . - /// - /// Dapr Secret Store name. - /// Indicates whether any key delimiters should be replaced with the delimiter ":". - /// The secrets to retrieve. - /// Dapr client used to retrieve Secrets - public DaprSecretStoreConfigurationProvider( - string store, - bool normalizeKey, - IEnumerable secretDescriptors, - DaprClient client) : this(store, normalizeKey, null, secretDescriptors, client, DefaultSidecarWaitTimeout) - { - } + private readonly DaprClient client; - /// - /// Creates a new instance of . - /// - /// Dapr Secret Store name. - /// Indicates whether any key delimiters should be replaced with the delimiter ":". - /// A collection of delimiters that will be replaced by ':' in the key of every secret. - /// The secrets to retrieve. - /// Dapr client used to retrieve Secrets - public DaprSecretStoreConfigurationProvider( - string store, - bool normalizeKey, - IList? keyDelimiters, - IEnumerable secretDescriptors, - DaprClient client) : this(store, normalizeKey, keyDelimiters, secretDescriptors, client, DefaultSidecarWaitTimeout) - { - } + private readonly TimeSpan sidecarWaitTimeout; - /// - /// Creates a new instance of . - /// - /// Dapr Secret Store name. - /// Indicates whether any key delimiters should be replaced with the delimiter ":". - /// A collection of delimiters that will be replaced by ':' in the key of every secret. - /// The secrets to retrieve. - /// Dapr client used to retrieve Secrets - /// The used to configure the timeout waiting for Dapr. - public DaprSecretStoreConfigurationProvider( - string store, - bool normalizeKey, - IList? keyDelimiters, - IEnumerable secretDescriptors, - DaprClient client, - TimeSpan sidecarWaitTimeout) - { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); + /// + /// Creates a new instance of . + /// + /// Dapr Secret Store name. + /// Indicates whether any key delimiters should be replaced with the delimiter ":". + /// The secrets to retrieve. + /// Dapr client used to retrieve Secrets + public DaprSecretStoreConfigurationProvider( + string store, + bool normalizeKey, + IEnumerable secretDescriptors, + DaprClient client) : this(store, normalizeKey, null, secretDescriptors, client, DefaultSidecarWaitTimeout) + { + } - if (secretDescriptors.Count() == 0) - { - throw new ArgumentException("No secret descriptor was provided", nameof(secretDescriptors)); - } + /// + /// Creates a new instance of . + /// + /// Dapr Secret Store name. + /// Indicates whether any key delimiters should be replaced with the delimiter ":". + /// A collection of delimiters that will be replaced by ':' in the key of every secret. + /// The secrets to retrieve. + /// Dapr client used to retrieve Secrets + public DaprSecretStoreConfigurationProvider( + string store, + bool normalizeKey, + IList? keyDelimiters, + IEnumerable secretDescriptors, + DaprClient client) : this(store, normalizeKey, keyDelimiters, secretDescriptors, client, DefaultSidecarWaitTimeout) + { + } - this.store = store; - this.normalizeKey = normalizeKey; - this.keyDelimiters = keyDelimiters; - this.secretDescriptors = secretDescriptors; - this.client = client; - this.sidecarWaitTimeout = sidecarWaitTimeout; - } + /// + /// Creates a new instance of . + /// + /// Dapr Secret Store name. + /// Indicates whether any key delimiters should be replaced with the delimiter ":". + /// A collection of delimiters that will be replaced by ':' in the key of every secret. + /// The secrets to retrieve. + /// Dapr client used to retrieve Secrets + /// The used to configure the timeout waiting for Dapr. + public DaprSecretStoreConfigurationProvider( + string store, + bool normalizeKey, + IList? keyDelimiters, + IEnumerable secretDescriptors, + DaprClient client, + TimeSpan sidecarWaitTimeout) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); - /// - /// Creates a new instance of . - /// - /// Dapr Secret Store name. - /// Indicates whether any key delimiters should be replaced with the delimiter ":". - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// Dapr client used to retrieve Secrets - public DaprSecretStoreConfigurationProvider( - string store, - bool normalizeKey, - IReadOnlyDictionary? metadata, - DaprClient client) : this(store, normalizeKey, null, metadata, client, DefaultSidecarWaitTimeout) + if (secretDescriptors.Count() == 0) { + throw new ArgumentException("No secret descriptor was provided", nameof(secretDescriptors)); } - /// - /// Creates a new instance of . - /// - /// Dapr Secret Store name. - /// Indicates whether any key delimiters should be replaced with the delimiter ":". - /// A collection of delimiters that will be replaced by ':' in the key of every secret. - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// Dapr client used to retrieve Secrets - public DaprSecretStoreConfigurationProvider( - string store, - bool normalizeKey, - IList? keyDelimiters, - IReadOnlyDictionary? metadata, - DaprClient client) : this(store, normalizeKey, keyDelimiters, metadata, client, DefaultSidecarWaitTimeout) - { - } + this.store = store; + this.normalizeKey = normalizeKey; + this.keyDelimiters = keyDelimiters; + this.secretDescriptors = secretDescriptors; + this.client = client; + this.sidecarWaitTimeout = sidecarWaitTimeout; + } - /// - /// Creates a new instance of . - /// - /// Dapr Secret Store name. - /// Indicates whether any key delimiters should be replaced with the delimiter ":". - /// A collection of delimiters that will be replaced by ':' in the key of every secret. - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// Dapr client used to retrieve Secrets - /// The used to configure the timeout waiting for Dapr. - public DaprSecretStoreConfigurationProvider( - string store, - bool normalizeKey, - IList? keyDelimiters, - IReadOnlyDictionary? metadata, - DaprClient client, - TimeSpan sidecarWaitTimeout) - { - ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); - ArgumentVerifier.ThrowIfNull(client, nameof(client)); - - this.store = store; - this.normalizeKey = normalizeKey; - this.keyDelimiters = keyDelimiters; - this.metadata = metadata; - this.client = client; - this.sidecarWaitTimeout = sidecarWaitTimeout; - } + /// + /// Creates a new instance of . + /// + /// Dapr Secret Store name. + /// Indicates whether any key delimiters should be replaced with the delimiter ":". + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// Dapr client used to retrieve Secrets + public DaprSecretStoreConfigurationProvider( + string store, + bool normalizeKey, + IReadOnlyDictionary? metadata, + DaprClient client) : this(store, normalizeKey, null, metadata, client, DefaultSidecarWaitTimeout) + { + } + + /// + /// Creates a new instance of . + /// + /// Dapr Secret Store name. + /// Indicates whether any key delimiters should be replaced with the delimiter ":". + /// A collection of delimiters that will be replaced by ':' in the key of every secret. + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// Dapr client used to retrieve Secrets + public DaprSecretStoreConfigurationProvider( + string store, + bool normalizeKey, + IList? keyDelimiters, + IReadOnlyDictionary? metadata, + DaprClient client) : this(store, normalizeKey, keyDelimiters, metadata, client, DefaultSidecarWaitTimeout) + { + } - private string NormalizeKey(string key) + /// + /// Creates a new instance of . + /// + /// Dapr Secret Store name. + /// Indicates whether any key delimiters should be replaced with the delimiter ":". + /// A collection of delimiters that will be replaced by ':' in the key of every secret. + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// Dapr client used to retrieve Secrets + /// The used to configure the timeout waiting for Dapr. + public DaprSecretStoreConfigurationProvider( + string store, + bool normalizeKey, + IList? keyDelimiters, + IReadOnlyDictionary? metadata, + DaprClient client, + TimeSpan sidecarWaitTimeout) + { + ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store)); + ArgumentVerifier.ThrowIfNull(client, nameof(client)); + + this.store = store; + this.normalizeKey = normalizeKey; + this.keyDelimiters = keyDelimiters; + this.metadata = metadata; + this.client = client; + this.sidecarWaitTimeout = sidecarWaitTimeout; + } + + private string NormalizeKey(string key) + { + if (this.keyDelimiters?.Count > 0) { - if (this.keyDelimiters?.Count > 0) + foreach (var keyDelimiter in this.keyDelimiters) { - foreach (var keyDelimiter in this.keyDelimiters) - { - key = key.Replace(keyDelimiter, ConfigurationPath.KeyDelimiter); - } + key = key.Replace(keyDelimiter, ConfigurationPath.KeyDelimiter); } - - return key; } - /// - /// Loads the configuration by calling the asynchronous LoadAsync method and blocking the calling - /// thread until the operation is completed. - /// - public override void Load() => LoadAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + return key; + } - private async Task LoadAsync() - { - var data = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + /// + /// Loads the configuration by calling the asynchronous LoadAsync method and blocking the calling + /// thread until the operation is completed. + /// + public override void Load() => LoadAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - // Wait for the Dapr Sidecar to report healthy before attempting to fetch secrets. - using (var tokenSource = new CancellationTokenSource(sidecarWaitTimeout)) - { - await client.WaitForSidecarAsync(tokenSource.Token); - } + private async Task LoadAsync() + { + var data = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + // Wait for the Dapr Sidecar to report healthy before attempting to fetch secrets. + using (var tokenSource = new CancellationTokenSource(sidecarWaitTimeout)) + { + await client.WaitForSidecarAsync(tokenSource.Token); + } - if (secretDescriptors != null) + if (secretDescriptors != null) + { + foreach (var secretDescriptor in secretDescriptors) { - foreach (var secretDescriptor in secretDescriptors) - { - Dictionary result; + Dictionary result; - try - { - result = await client - .GetSecretAsync(store, secretDescriptor.SecretKey, secretDescriptor.Metadata) - .ConfigureAwait(false); - } - catch (DaprException) + try + { + result = await client + .GetSecretAsync(store, secretDescriptor.SecretKey, secretDescriptor.Metadata) + .ConfigureAwait(false); + } + catch (DaprException) + { + if (secretDescriptor.IsRequired) { - if (secretDescriptor.IsRequired) - { - throw; - } - result = new Dictionary(); + throw; } + result = new Dictionary(); + } - foreach (var key in result.Keys) + foreach (var key in result.Keys) + { + if (data.ContainsKey(key)) { - if (data.ContainsKey(key)) - { - throw new InvalidOperationException( - $"A duplicate key '{key}' was found in the secret store '{store}'. Please remove any duplicates from your secret store."); - } + throw new InvalidOperationException( + $"A duplicate key '{key}' was found in the secret store '{store}'. Please remove any duplicates from your secret store."); + } - // The name of the key "as desired" by the user based on the descriptor. - // - // NOTE: This should vary only if a single secret of the same name is returned. - string desiredKey = StringComparer.Ordinal.Equals(key, secretDescriptor.SecretKey) ? secretDescriptor.SecretName : key; + // The name of the key "as desired" by the user based on the descriptor. + // + // NOTE: This should vary only if a single secret of the same name is returned. + string desiredKey = StringComparer.Ordinal.Equals(key, secretDescriptor.SecretKey) ? secretDescriptor.SecretName : key; - // The name of the key normalized based on the configured delimiters. - string normalizedKey = normalizeKey ? NormalizeKey(desiredKey) : desiredKey; + // The name of the key normalized based on the configured delimiters. + string normalizedKey = normalizeKey ? NormalizeKey(desiredKey) : desiredKey; - data.Add(normalizedKey, result[key]); - } + data.Add(normalizedKey, result[key]); } - - Data = data; } - else + + Data = data; + } + else + { + var result = await client.GetBulkSecretAsync(store, metadata).ConfigureAwait(false); + foreach (var key in result.Keys) { - var result = await client.GetBulkSecretAsync(store, metadata).ConfigureAwait(false); - foreach (var key in result.Keys) + foreach (var secret in result[key]) { - foreach (var secret in result[key]) + if (data.ContainsKey(secret.Key)) { - if (data.ContainsKey(secret.Key)) - { - throw new InvalidOperationException($"A duplicate key '{secret.Key}' was found in the secret store '{store}'. Please remove any duplicates from your secret store."); - } - - data.Add(normalizeKey ? NormalizeKey(secret.Key) : secret.Key, secret.Value); + throw new InvalidOperationException($"A duplicate key '{secret.Key}' was found in the secret store '{store}'. Please remove any duplicates from your secret store."); } + + data.Add(normalizeKey ? NormalizeKey(secret.Key) : secret.Key, secret.Value); } - Data = data; } + Data = data; } } -} +} \ No newline at end of file diff --git a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs index eee3d0b2a..de73c8518 100644 --- a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs +++ b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs @@ -16,65 +16,64 @@ using Dapr.Client; using Microsoft.Extensions.Configuration; -namespace Dapr.Extensions.Configuration.DaprSecretStore +namespace Dapr.Extensions.Configuration.DaprSecretStore; + +/// +/// Represents Dapr Secret Store as an . +/// +public class DaprSecretStoreConfigurationSource : IConfigurationSource { /// - /// Represents Dapr Secret Store as an . + /// Gets or sets the store name. /// - public class DaprSecretStoreConfigurationSource : IConfigurationSource - { - /// - /// Gets or sets the store name. - /// - public string Store { get; set; } = default!; + public string Store { get; set; } = default!; - /// - /// Gets or sets a value indicating whether any key delimiters should be replaced with the delimiter ":". - /// Default value true. - /// - public bool NormalizeKey { get; set; } = true; + /// + /// Gets or sets a value indicating whether any key delimiters should be replaced with the delimiter ":". + /// Default value true. + /// + public bool NormalizeKey { get; set; } = true; - /// - /// Gets or sets the custom key delimiters. Contains the '__' delimiter by default. - /// - public IList KeyDelimiters { get; set; } = new List { "__" }; + /// + /// Gets or sets the custom key delimiters. Contains the '__' delimiter by default. + /// + public IList KeyDelimiters { get; set; } = new List { "__" }; - /// - /// Gets or sets the secret descriptors. - /// - public IEnumerable? SecretDescriptors { get; set; } + /// + /// Gets or sets the secret descriptors. + /// + public IEnumerable? SecretDescriptors { get; set; } - /// - /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. - /// - public IReadOnlyDictionary? Metadata { get; set; } + /// + /// A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used. + /// + public IReadOnlyDictionary? Metadata { get; set; } - /// - /// Gets or sets the http client. - /// - public DaprClient Client { get; set; } = default!; + /// + /// Gets or sets the http client. + /// + public DaprClient Client { get; set; } = default!; - /// - /// Gets or sets the that is used to control the timeout waiting for the Dapr sidecar to become healthly. - /// - public TimeSpan? SidecarWaitTimeout { get; set; } + /// + /// Gets or sets the that is used to control the timeout waiting for the Dapr sidecar to become healthly. + /// + public TimeSpan? SidecarWaitTimeout { get; set; } - /// - public IConfigurationProvider Build(IConfigurationBuilder builder) + /// + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + if (SecretDescriptors != null) { - if (SecretDescriptors != null) - { - if (Metadata != null) - { - throw new ArgumentException($"{nameof(Metadata)} must be null when {nameof(SecretDescriptors)} is set", nameof(Metadata)); - } - - return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, SecretDescriptors, Client, SidecarWaitTimeout ?? DaprSecretStoreConfigurationProvider.DefaultSidecarWaitTimeout); - } - else + if (Metadata != null) { - return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, Metadata, Client, SidecarWaitTimeout ?? DaprSecretStoreConfigurationProvider.DefaultSidecarWaitTimeout); + throw new ArgumentException($"{nameof(Metadata)} must be null when {nameof(SecretDescriptors)} is set", nameof(Metadata)); } + + return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, SecretDescriptors, Client, SidecarWaitTimeout ?? DaprSecretStoreConfigurationProvider.DefaultSidecarWaitTimeout); + } + else + { + return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, Metadata, Client, SidecarWaitTimeout ?? DaprSecretStoreConfigurationProvider.DefaultSidecarWaitTimeout); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Jobs/DaprJobsClientBuilder.cs b/src/Dapr.Jobs/DaprJobsClientBuilder.cs index 509486a1e..12a66a892 100644 --- a/src/Dapr.Jobs/DaprJobsClientBuilder.cs +++ b/src/Dapr.Jobs/DaprJobsClientBuilder.cs @@ -20,16 +20,9 @@ namespace Dapr.Jobs; /// /// Builds a . /// -public sealed class DaprJobsClientBuilder : DaprGenericClientBuilder +/// An optional instance of . +public sealed class DaprJobsClientBuilder(IConfiguration? configuration = null) : DaprGenericClientBuilder(configuration) { - /// - /// Used to initialize a new instance of . - /// - /// An optional instance of . - public DaprJobsClientBuilder(IConfiguration? configuration = null) : base(configuration) - { - } - /// /// Builds the client instance from the properties of the builder. /// diff --git a/src/Dapr.Messaging/PublishSubscribe/DaprPublishSubscribeClientBuilder.cs b/src/Dapr.Messaging/PublishSubscribe/DaprPublishSubscribeClientBuilder.cs index 829bab75d..691ff9d38 100644 --- a/src/Dapr.Messaging/PublishSubscribe/DaprPublishSubscribeClientBuilder.cs +++ b/src/Dapr.Messaging/PublishSubscribe/DaprPublishSubscribeClientBuilder.cs @@ -20,16 +20,9 @@ namespace Dapr.Messaging.PublishSubscribe; /// /// Builds a . /// -public sealed class DaprPublishSubscribeClientBuilder : DaprGenericClientBuilder +/// An optional instance of . +public sealed class DaprPublishSubscribeClientBuilder(IConfiguration? configuration = null) : DaprGenericClientBuilder(configuration) { - /// - /// Used to initialize a new instance of the . - /// - /// An optional instance of . - public DaprPublishSubscribeClientBuilder(IConfiguration? configuration = null) : base(configuration) - { - } - /// /// Builds the client instance from the properties of the builder. /// diff --git a/src/Dapr.Workflow/DaprWorkflowActivityContext.cs b/src/Dapr.Workflow/DaprWorkflowActivityContext.cs index cf902dea4..acd90f008 100644 --- a/src/Dapr.Workflow/DaprWorkflowActivityContext.cs +++ b/src/Dapr.Workflow/DaprWorkflowActivityContext.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow +namespace Dapr.Workflow; + +using System; +using Microsoft.DurableTask; + +/// +/// Defines properties and methods for task activity context objects. +/// +public class DaprWorkflowActivityContext : WorkflowActivityContext { - using System; - using Microsoft.DurableTask; + readonly TaskActivityContext innerContext; - /// - /// Defines properties and methods for task activity context objects. - /// - public class DaprWorkflowActivityContext : WorkflowActivityContext + internal DaprWorkflowActivityContext(TaskActivityContext innerContext) { - readonly TaskActivityContext innerContext; - - internal DaprWorkflowActivityContext(TaskActivityContext innerContext) - { - this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); - } + this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); + } - /// - public override TaskName Name => this.innerContext.Name; + /// + public override TaskName Name => this.innerContext.Name; - /// - public override string InstanceId => this.innerContext.InstanceId; - } -} + /// + public override string InstanceId => this.innerContext.InstanceId; +} \ No newline at end of file diff --git a/src/Dapr.Workflow/DaprWorkflowClient.cs b/src/Dapr.Workflow/DaprWorkflowClient.cs index e4c88f0ef..6f9e05bde 100644 --- a/src/Dapr.Workflow/DaprWorkflowClient.cs +++ b/src/Dapr.Workflow/DaprWorkflowClient.cs @@ -17,287 +17,286 @@ using Microsoft.DurableTask; using Microsoft.DurableTask.Client; -namespace Dapr.Workflow +namespace Dapr.Workflow; + +/// +/// Defines client operations for managing Dapr Workflow instances. +/// +/// +/// This is an alternative to the general purpose Dapr client. It uses a gRPC connection to send +/// commands directly to the workflow engine, bypassing the Dapr API layer. +/// +public class DaprWorkflowClient : IAsyncDisposable { + readonly DurableTaskClient innerClient; + /// - /// Defines client operations for managing Dapr Workflow instances. + /// Initializes a new instance of the class. /// - /// - /// This is an alternative to the general purpose Dapr client. It uses a gRPC connection to send - /// commands directly to the workflow engine, bypassing the Dapr API layer. - /// - public class DaprWorkflowClient : IAsyncDisposable + /// The Durable Task client used to communicate with the Dapr sidecar. + /// Thrown if is null. + public DaprWorkflowClient(DurableTaskClient innerClient) { - readonly DurableTaskClient innerClient; - - /// - /// Initializes a new instance of the class. - /// - /// The Durable Task client used to communicate with the Dapr sidecar. - /// Thrown if is null. - public DaprWorkflowClient(DurableTaskClient innerClient) - { - this.innerClient = innerClient ?? throw new ArgumentNullException(nameof(innerClient)); - } + this.innerClient = innerClient ?? throw new ArgumentNullException(nameof(innerClient)); + } - /// - /// Schedules a new workflow instance for execution. - /// - /// The name of the workflow to schedule. - /// - /// The unique ID of the workflow instance to schedule. If not specified, a new GUID value is used. - /// - /// - /// The time when the workflow instance should start executing. If not specified or if a date-time in the past - /// is specified, the workflow instance will be scheduled immediately. - /// - /// - /// The optional input to pass to the scheduled workflow instance. This must be a serializable value. - /// - public Task ScheduleNewWorkflowAsync( - string name, - string? instanceId = null, - object? input = null, - DateTime? startTime = null) - { - StartOrchestrationOptions options = new(instanceId, startTime); - return this.innerClient.ScheduleNewOrchestrationInstanceAsync(name, input, options); - } + /// + /// Schedules a new workflow instance for execution. + /// + /// The name of the workflow to schedule. + /// + /// The unique ID of the workflow instance to schedule. If not specified, a new GUID value is used. + /// + /// + /// The time when the workflow instance should start executing. If not specified or if a date-time in the past + /// is specified, the workflow instance will be scheduled immediately. + /// + /// + /// The optional input to pass to the scheduled workflow instance. This must be a serializable value. + /// + public Task ScheduleNewWorkflowAsync( + string name, + string? instanceId = null, + object? input = null, + DateTime? startTime = null) + { + StartOrchestrationOptions options = new(instanceId, startTime); + return this.innerClient.ScheduleNewOrchestrationInstanceAsync(name, input, options); + } - /// - /// Fetches runtime state for the specified workflow instance. - /// - /// The unique ID of the workflow instance to fetch. - /// - /// Specify true to fetch the workflow instance's inputs, outputs, and custom status, or false to - /// omit them. Defaults to true. - /// - public async Task GetWorkflowStateAsync(string instanceId, bool getInputsAndOutputs = true) - { - OrchestrationMetadata? metadata = await this.innerClient.GetInstancesAsync( - instanceId, - getInputsAndOutputs); - return new WorkflowState(metadata); - } + /// + /// Fetches runtime state for the specified workflow instance. + /// + /// The unique ID of the workflow instance to fetch. + /// + /// Specify true to fetch the workflow instance's inputs, outputs, and custom status, or false to + /// omit them. Defaults to true. + /// + public async Task GetWorkflowStateAsync(string instanceId, bool getInputsAndOutputs = true) + { + OrchestrationMetadata? metadata = await this.innerClient.GetInstancesAsync( + instanceId, + getInputsAndOutputs); + return new WorkflowState(metadata); + } - /// - /// Waits for a workflow to start running and returns a object that contains metadata - /// about the started workflow. - /// - /// - /// - /// A "started" workflow instance is any instance not in the state. - /// - /// This method will return a completed task if the workflow has already started running or has already completed. - /// - /// - /// The unique ID of the workflow instance to wait for. - /// - /// Specify true to fetch the workflow instance's inputs, outputs, and custom status, or false to - /// omit them. Setting this value to false can help minimize the network bandwidth, serialization, and memory costs - /// associated with fetching the instance metadata. - /// - /// A that can be used to cancel the wait operation. - /// - /// Returns a record that describes the workflow instance and its execution - /// status. If the specified workflow isn't found, the value will be false. - /// - public async Task WaitForWorkflowStartAsync( - string instanceId, - bool getInputsAndOutputs = true, - CancellationToken cancellation = default) - { - OrchestrationMetadata metadata = await this.innerClient.WaitForInstanceStartAsync( - instanceId, - getInputsAndOutputs, - cancellation); - return new WorkflowState(metadata); - } + /// + /// Waits for a workflow to start running and returns a object that contains metadata + /// about the started workflow. + /// + /// + /// + /// A "started" workflow instance is any instance not in the state. + /// + /// This method will return a completed task if the workflow has already started running or has already completed. + /// + /// + /// The unique ID of the workflow instance to wait for. + /// + /// Specify true to fetch the workflow instance's inputs, outputs, and custom status, or false to + /// omit them. Setting this value to false can help minimize the network bandwidth, serialization, and memory costs + /// associated with fetching the instance metadata. + /// + /// A that can be used to cancel the wait operation. + /// + /// Returns a record that describes the workflow instance and its execution + /// status. If the specified workflow isn't found, the value will be false. + /// + public async Task WaitForWorkflowStartAsync( + string instanceId, + bool getInputsAndOutputs = true, + CancellationToken cancellation = default) + { + OrchestrationMetadata metadata = await this.innerClient.WaitForInstanceStartAsync( + instanceId, + getInputsAndOutputs, + cancellation); + return new WorkflowState(metadata); + } - /// - /// Waits for a workflow to complete and returns a - /// object that contains metadata about the started instance. - /// - /// - /// - /// A "completed" workflow instance is any instance in one of the terminal states. For example, the - /// , , or - /// states. - /// - /// Workflows are long-running and could take hours, days, or months before completing. - /// Workflows can also be eternal, in which case they'll never complete unless terminated. - /// In such cases, this call may block indefinitely, so care must be taken to ensure appropriate timeouts are - /// enforced using the parameter. - /// - /// If a workflow instance is already complete when this method is called, the method will return immediately. - /// - /// - /// - public async Task WaitForWorkflowCompletionAsync( - string instanceId, - bool getInputsAndOutputs = true, - CancellationToken cancellation = default) - { - OrchestrationMetadata metadata = await this.innerClient.WaitForInstanceCompletionAsync( - instanceId, - getInputsAndOutputs, - cancellation); - return new WorkflowState(metadata); - } + /// + /// Waits for a workflow to complete and returns a + /// object that contains metadata about the started instance. + /// + /// + /// + /// A "completed" workflow instance is any instance in one of the terminal states. For example, the + /// , , or + /// states. + /// + /// Workflows are long-running and could take hours, days, or months before completing. + /// Workflows can also be eternal, in which case they'll never complete unless terminated. + /// In such cases, this call may block indefinitely, so care must be taken to ensure appropriate timeouts are + /// enforced using the parameter. + /// + /// If a workflow instance is already complete when this method is called, the method will return immediately. + /// + /// + /// + public async Task WaitForWorkflowCompletionAsync( + string instanceId, + bool getInputsAndOutputs = true, + CancellationToken cancellation = default) + { + OrchestrationMetadata metadata = await this.innerClient.WaitForInstanceCompletionAsync( + instanceId, + getInputsAndOutputs, + cancellation); + return new WorkflowState(metadata); + } - /// - /// Terminates a running workflow instance and updates its runtime status to - /// . - /// - /// - /// - /// This method internally enqueues a "terminate" message in the task hub. When the task hub worker processes - /// this message, it will update the runtime status of the target instance to - /// . You can use the - /// to wait for the instance to reach - /// the terminated state. - /// - /// - /// Terminating a workflow terminates all of the child workflow instances that were created by the target. But it - /// has no effect on any in-flight activity function executions - /// that were started by the terminated instance. Those actions will continue to run - /// without interruption. However, their results will be discarded. - /// - /// At the time of writing, there is no way to terminate an in-flight activity execution. - /// - /// - /// The ID of the workflow instance to terminate. - /// The optional output to set for the terminated workflow instance. - /// - /// The cancellation token. This only cancels enqueueing the termination request to the backend. Does not abort - /// termination of the workflow once enqueued. - /// - /// A task that completes when the terminate message is enqueued. - public Task TerminateWorkflowAsync( - string instanceId, - string? output = null, - CancellationToken cancellation = default) - { - TerminateInstanceOptions options = new TerminateInstanceOptions { - Output = output, - Recursive = true, - }; - return this.innerClient.TerminateInstanceAsync(instanceId, options, cancellation); - } + /// + /// Terminates a running workflow instance and updates its runtime status to + /// . + /// + /// + /// + /// This method internally enqueues a "terminate" message in the task hub. When the task hub worker processes + /// this message, it will update the runtime status of the target instance to + /// . You can use the + /// to wait for the instance to reach + /// the terminated state. + /// + /// + /// Terminating a workflow terminates all of the child workflow instances that were created by the target. But it + /// has no effect on any in-flight activity function executions + /// that were started by the terminated instance. Those actions will continue to run + /// without interruption. However, their results will be discarded. + /// + /// At the time of writing, there is no way to terminate an in-flight activity execution. + /// + /// + /// The ID of the workflow instance to terminate. + /// The optional output to set for the terminated workflow instance. + /// + /// The cancellation token. This only cancels enqueueing the termination request to the backend. Does not abort + /// termination of the workflow once enqueued. + /// + /// A task that completes when the terminate message is enqueued. + public Task TerminateWorkflowAsync( + string instanceId, + string? output = null, + CancellationToken cancellation = default) + { + TerminateInstanceOptions options = new TerminateInstanceOptions { + Output = output, + Recursive = true, + }; + return this.innerClient.TerminateInstanceAsync(instanceId, options, cancellation); + } - /// - /// Sends an event notification message to a waiting workflow instance. - /// - /// - /// - /// In order to handle the event, the target workflow instance must be waiting for an - /// event named using the - /// API. - /// If the target workflow instance is not yet waiting for an event named , - /// then the event will be saved in the workflow instance state and dispatched immediately when the - /// workflow calls . - /// This event saving occurs even if the workflow has canceled its wait operation before the event was received. - /// - /// Workflows can wait for the same event name multiple times, so sending multiple events with the same name is - /// allowed. Each external event received by an workflow will complete just one task returned by the - /// method. - /// - /// Raised events for a completed or non-existent workflow instance will be silently discarded. - /// - /// - /// The ID of the workflow instance that will handle the event. - /// The name of the event. Event names are case-insensitive. - /// The serializable data payload to include with the event. - /// - /// The cancellation token. This only cancels enqueueing the event to the backend. Does not abort sending the event - /// once enqueued. - /// - /// A task that completes when the event notification message has been enqueued. - /// - /// Thrown if or is null or empty. - /// - public Task RaiseEventAsync( - string instanceId, - string eventName, - object? eventPayload = null, - CancellationToken cancellation = default) - { - return this.innerClient.RaiseEventAsync(instanceId, eventName, eventPayload, cancellation); - } + /// + /// Sends an event notification message to a waiting workflow instance. + /// + /// + /// + /// In order to handle the event, the target workflow instance must be waiting for an + /// event named using the + /// API. + /// If the target workflow instance is not yet waiting for an event named , + /// then the event will be saved in the workflow instance state and dispatched immediately when the + /// workflow calls . + /// This event saving occurs even if the workflow has canceled its wait operation before the event was received. + /// + /// Workflows can wait for the same event name multiple times, so sending multiple events with the same name is + /// allowed. Each external event received by an workflow will complete just one task returned by the + /// method. + /// + /// Raised events for a completed or non-existent workflow instance will be silently discarded. + /// + /// + /// The ID of the workflow instance that will handle the event. + /// The name of the event. Event names are case-insensitive. + /// The serializable data payload to include with the event. + /// + /// The cancellation token. This only cancels enqueueing the event to the backend. Does not abort sending the event + /// once enqueued. + /// + /// A task that completes when the event notification message has been enqueued. + /// + /// Thrown if or is null or empty. + /// + public Task RaiseEventAsync( + string instanceId, + string eventName, + object? eventPayload = null, + CancellationToken cancellation = default) + { + return this.innerClient.RaiseEventAsync(instanceId, eventName, eventPayload, cancellation); + } - /// - /// Suspends a workflow instance, halting processing of it until - /// is used to resume the workflow. - /// - /// The instance ID of the workflow to suspend. - /// The optional suspension reason. - /// - /// A that can be used to cancel the suspend operation. Note, cancelling this token - /// does not resume the workflow if suspend was successful. - /// - /// A task that completes when the suspend has been committed to the backend. - public Task SuspendWorkflowAsync( - string instanceId, - string? reason = null, - CancellationToken cancellation = default) - { - return this.innerClient.SuspendInstanceAsync(instanceId, reason, cancellation); - } + /// + /// Suspends a workflow instance, halting processing of it until + /// is used to resume the workflow. + /// + /// The instance ID of the workflow to suspend. + /// The optional suspension reason. + /// + /// A that can be used to cancel the suspend operation. Note, cancelling this token + /// does not resume the workflow if suspend was successful. + /// + /// A task that completes when the suspend has been committed to the backend. + public Task SuspendWorkflowAsync( + string instanceId, + string? reason = null, + CancellationToken cancellation = default) + { + return this.innerClient.SuspendInstanceAsync(instanceId, reason, cancellation); + } - /// - /// Resumes a workflow instance that was suspended via . - /// - /// The instance ID of the workflow to resume. - /// The optional resume reason. - /// - /// A that can be used to cancel the resume operation. Note, cancelling this token - /// does not re-suspend the workflow if resume was successful. - /// - /// A task that completes when the resume has been committed to the backend. - public Task ResumeWorkflowAsync( - string instanceId, - string? reason = null, - CancellationToken cancellation = default) - { - return this.innerClient.ResumeInstanceAsync(instanceId, reason, cancellation); - } + /// + /// Resumes a workflow instance that was suspended via . + /// + /// The instance ID of the workflow to resume. + /// The optional resume reason. + /// + /// A that can be used to cancel the resume operation. Note, cancelling this token + /// does not re-suspend the workflow if resume was successful. + /// + /// A task that completes when the resume has been committed to the backend. + public Task ResumeWorkflowAsync( + string instanceId, + string? reason = null, + CancellationToken cancellation = default) + { + return this.innerClient.ResumeInstanceAsync(instanceId, reason, cancellation); + } - /// - /// Purges workflow instance state from the workflow state store. - /// - /// - /// - /// This method can be used to permanently delete workflow metadata from the underlying state store, - /// including any stored inputs, outputs, and workflow history records. This is often useful for implementing - /// data retention policies and for keeping storage costs minimal. Only workflow instances in the - /// , , or - /// state can be purged. - /// - /// - /// Purging a workflow purges all of the child workflows that were created by the target. - /// - /// - /// The unique ID of the workflow instance to purge. - /// - /// A that can be used to cancel the purge operation. - /// - /// - /// Returns a task that completes when the purge operation has completed. The value of this task will be - /// true if the workflow state was found and purged successfully; false otherwise. - /// - public async Task PurgeInstanceAsync(string instanceId, CancellationToken cancellation = default) - { - PurgeInstanceOptions options = new PurgeInstanceOptions {Recursive = true}; - PurgeResult result = await this.innerClient.PurgeInstanceAsync(instanceId, options, cancellation); - return result.PurgedInstanceCount > 0; - } + /// + /// Purges workflow instance state from the workflow state store. + /// + /// + /// + /// This method can be used to permanently delete workflow metadata from the underlying state store, + /// including any stored inputs, outputs, and workflow history records. This is often useful for implementing + /// data retention policies and for keeping storage costs minimal. Only workflow instances in the + /// , , or + /// state can be purged. + /// + /// + /// Purging a workflow purges all of the child workflows that were created by the target. + /// + /// + /// The unique ID of the workflow instance to purge. + /// + /// A that can be used to cancel the purge operation. + /// + /// + /// Returns a task that completes when the purge operation has completed. The value of this task will be + /// true if the workflow state was found and purged successfully; false otherwise. + /// + public async Task PurgeInstanceAsync(string instanceId, CancellationToken cancellation = default) + { + PurgeInstanceOptions options = new PurgeInstanceOptions {Recursive = true}; + PurgeResult result = await this.innerClient.PurgeInstanceAsync(instanceId, options, cancellation); + return result.PurgedInstanceCount > 0; + } - /// - /// Disposes any unmanaged resources associated with this client. - /// - public ValueTask DisposeAsync() - { - return ((IAsyncDisposable)this.innerClient).DisposeAsync(); - } + /// + /// Disposes any unmanaged resources associated with this client. + /// + public ValueTask DisposeAsync() + { + return ((IAsyncDisposable)this.innerClient).DisposeAsync(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Workflow/DaprWorkflowContext.cs b/src/Dapr.Workflow/DaprWorkflowContext.cs index 55c965955..4e51a55f7 100644 --- a/src/Dapr.Workflow/DaprWorkflowContext.cs +++ b/src/Dapr.Workflow/DaprWorkflowContext.cs @@ -13,133 +13,132 @@ using Microsoft.Extensions.Logging; -namespace Dapr.Workflow +namespace Dapr.Workflow; + +using System; +using Microsoft.DurableTask; +using System.Threading.Tasks; +using System.Threading; + +class DaprWorkflowContext : WorkflowContext { - using System; - using Microsoft.DurableTask; - using System.Threading.Tasks; - using System.Threading; + readonly TaskOrchestrationContext innerContext; - class DaprWorkflowContext : WorkflowContext + internal DaprWorkflowContext(TaskOrchestrationContext innerContext) { - readonly TaskOrchestrationContext innerContext; - - internal DaprWorkflowContext(TaskOrchestrationContext innerContext) - { - this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); - } + this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); + } - public override string Name => this.innerContext.Name; + public override string Name => this.innerContext.Name; - public override string InstanceId => this.innerContext.InstanceId; + public override string InstanceId => this.innerContext.InstanceId; - public override DateTime CurrentUtcDateTime => this.innerContext.CurrentUtcDateTime; + public override DateTime CurrentUtcDateTime => this.innerContext.CurrentUtcDateTime; - public override bool IsReplaying => this.innerContext.IsReplaying; + public override bool IsReplaying => this.innerContext.IsReplaying; - public override Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null) - { - return WrapExceptions(this.innerContext.CallActivityAsync(name, input, options?.ToDurableTaskOptions())); - } + public override Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null) + { + return WrapExceptions(this.innerContext.CallActivityAsync(name, input, options?.ToDurableTaskOptions())); + } - public override Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null) - { - return WrapExceptions(this.innerContext.CallActivityAsync(name, input, options?.ToDurableTaskOptions())); - } + public override Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null) + { + return WrapExceptions(this.innerContext.CallActivityAsync(name, input, options?.ToDurableTaskOptions())); + } - public override Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) - { - return this.innerContext.CreateTimer(delay, cancellationToken); - } + public override Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) + { + return this.innerContext.CreateTimer(delay, cancellationToken); + } - public override Task CreateTimer(DateTime fireAt, CancellationToken cancellationToken) - { - return this.innerContext.CreateTimer(fireAt, cancellationToken); - } + public override Task CreateTimer(DateTime fireAt, CancellationToken cancellationToken) + { + return this.innerContext.CreateTimer(fireAt, cancellationToken); + } - public override Task WaitForExternalEventAsync(string eventName, CancellationToken cancellationToken = default) - { - return this.innerContext.WaitForExternalEvent(eventName, cancellationToken); - } + public override Task WaitForExternalEventAsync(string eventName, CancellationToken cancellationToken = default) + { + return this.innerContext.WaitForExternalEvent(eventName, cancellationToken); + } - public override Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) - { - return this.innerContext.WaitForExternalEvent(eventName, timeout); - } + public override Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) + { + return this.innerContext.WaitForExternalEvent(eventName, timeout); + } - public override void SendEvent(string instanceId, string eventName, object payload) - { - this.innerContext.SendEvent(instanceId, eventName, payload); - } + public override void SendEvent(string instanceId, string eventName, object payload) + { + this.innerContext.SendEvent(instanceId, eventName, payload); + } - public override void SetCustomStatus(object? customStatus) - { - this.innerContext.SetCustomStatus(customStatus); - } + public override void SetCustomStatus(object? customStatus) + { + this.innerContext.SetCustomStatus(customStatus); + } - public override Task CallChildWorkflowAsync(string workflowName, object? input = null, ChildWorkflowTaskOptions? options = null) - { - return WrapExceptions(this.innerContext.CallSubOrchestratorAsync(workflowName, input, options?.ToDurableTaskOptions())); - } + public override Task CallChildWorkflowAsync(string workflowName, object? input = null, ChildWorkflowTaskOptions? options = null) + { + return WrapExceptions(this.innerContext.CallSubOrchestratorAsync(workflowName, input, options?.ToDurableTaskOptions())); + } - public override Task CallChildWorkflowAsync(string workflowName, object? input = null, ChildWorkflowTaskOptions? options = null) - { - return WrapExceptions(this.innerContext.CallSubOrchestratorAsync(workflowName, input, options?.ToDurableTaskOptions())); - } + public override Task CallChildWorkflowAsync(string workflowName, object? input = null, ChildWorkflowTaskOptions? options = null) + { + return WrapExceptions(this.innerContext.CallSubOrchestratorAsync(workflowName, input, options?.ToDurableTaskOptions())); + } + + public override void ContinueAsNew(object? newInput = null, bool preserveUnprocessedEvents = true) + { + this.innerContext.ContinueAsNew(newInput!, preserveUnprocessedEvents); + } + + public override Guid NewGuid() + { + return this.innerContext.NewGuid(); + } - public override void ContinueAsNew(object? newInput = null, bool preserveUnprocessedEvents = true) + /// + /// Returns an instance of that is replay-safe, meaning that the logger only + /// writes logs when the orchestrator is not replaying previous history. + /// + /// The logger's category name. + /// An instance of that is replay-safe. + public override ILogger CreateReplaySafeLogger(string categoryName) => + this.innerContext.CreateReplaySafeLogger(categoryName); + + /// + /// The type to derive the category name from. + public override ILogger CreateReplaySafeLogger(Type type) => + this.innerContext.CreateReplaySafeLogger(type); + + /// + /// The type to derive category name from. + public override ILogger CreateReplaySafeLogger() => + this.innerContext.CreateReplaySafeLogger(); + + static async Task WrapExceptions(Task task) + { + try { - this.innerContext.ContinueAsNew(newInput!, preserveUnprocessedEvents); + await task; } - - public override Guid NewGuid() + catch (TaskFailedException ex) { - return this.innerContext.NewGuid(); + var details = new WorkflowTaskFailureDetails(ex.FailureDetails); + throw new WorkflowTaskFailedException(ex.Message, details); } + } - /// - /// Returns an instance of that is replay-safe, meaning that the logger only - /// writes logs when the orchestrator is not replaying previous history. - /// - /// The logger's category name. - /// An instance of that is replay-safe. - public override ILogger CreateReplaySafeLogger(string categoryName) => - this.innerContext.CreateReplaySafeLogger(categoryName); - - /// - /// The type to derive the category name from. - public override ILogger CreateReplaySafeLogger(Type type) => - this.innerContext.CreateReplaySafeLogger(type); - - /// - /// The type to derive category name from. - public override ILogger CreateReplaySafeLogger() => - this.innerContext.CreateReplaySafeLogger(); - - static async Task WrapExceptions(Task task) + static async Task WrapExceptions(Task task) + { + try { - try - { - await task; - } - catch (TaskFailedException ex) - { - var details = new WorkflowTaskFailureDetails(ex.FailureDetails); - throw new WorkflowTaskFailedException(ex.Message, details); - } + return await task; } - - static async Task WrapExceptions(Task task) + catch (TaskFailedException ex) { - try - { - return await task; - } - catch (TaskFailedException ex) - { - var details = new WorkflowTaskFailureDetails(ex.FailureDetails); - throw new WorkflowTaskFailedException(ex.Message, details); - } + var details = new WorkflowTaskFailureDetails(ex.FailureDetails); + throw new WorkflowTaskFailedException(ex.Message, details); } } -} +} \ No newline at end of file diff --git a/src/Dapr.Workflow/Workflow.cs b/src/Dapr.Workflow/Workflow.cs index 6510414bf..b8d43b35c 100644 --- a/src/Dapr.Workflow/Workflow.cs +++ b/src/Dapr.Workflow/Workflow.cs @@ -11,127 +11,126 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow -{ - using System; - using System.Threading.Tasks; +namespace Dapr.Workflow; + +using System; +using System.Threading.Tasks; +/// +/// Common interface for workflow implementations. +/// +/// +/// Users should not implement workflows using this interface, directly. +/// Instead, should be used to implement workflows. +/// +public interface IWorkflow +{ /// - /// Common interface for workflow implementations. + /// Gets the type of the input parameter that this workflow accepts. /// - /// - /// Users should not implement workflows using this interface, directly. - /// Instead, should be used to implement workflows. - /// - public interface IWorkflow - { - /// - /// Gets the type of the input parameter that this workflow accepts. - /// - Type InputType { get; } + Type InputType { get; } - /// - /// Gets the type of the return value that this workflow produces. - /// - Type OutputType { get; } - - /// - /// Invokes the workflow with the specified context and input. - /// - /// The workflow's context. - /// The workflow's input. - /// Returns the workflow output as the result of a . - Task RunAsync(WorkflowContext context, object? input); - } + /// + /// Gets the type of the return value that this workflow produces. + /// + Type OutputType { get; } /// - /// Represents the base class for workflows. + /// Invokes the workflow with the specified context and input. /// - /// - /// - /// Workflows describe how actions are executed and the order in which actions are executed. Workflows - /// don't call into external services or do complex computation directly. Rather, they delegate these tasks to - /// activities, which perform the actual work. - /// - /// - /// Workflows can be scheduled using the Dapr client or by other workflows as child-workflows using the - /// method. - /// - /// - /// Workflows may be replayed multiple times to rebuild their local state after being reloaded into memory. - /// workflow code must therefore be deterministic to ensure no unexpected side effects from execution - /// replay. To account for this behavior, there are several coding constraints to be aware of: - /// - /// - /// A workflow must not generate random numbers or random GUIDs, get the current date, read environment - /// variables, or do anything else that might result in a different value if the code is replayed in the future. - /// Activities and built-in properties and methods on the parameter, like - /// and , - /// can be used to work around these restrictions. - /// - /// - /// Workflow logic must be executed on the workflow thread (the thread that calls . - /// Creating new threads, scheduling callbacks on worker pool threads, or awaiting non-workflow tasks is forbidden - /// and may result in failures or other unexpected behavior. Blocking the workflow thread may also result in unexpected - /// performance degredation. The use of await should be restricted to workflow tasks - i.e. tasks returned from - /// methods on the parameter object or tasks that wrap these workflow tasks, like - /// and . - /// - /// - /// Avoid infinite loops as they could cause the application to run out of memory. Instead, ensure that loops are - /// bounded or use to restart the workflow with a new input. - /// - /// - /// Avoid logging normally in the workflow code because log messages will be duplicated on each replay. - /// Instead, write log statements when is false. - /// - /// - /// - /// - /// Workflow code is tightly coupled with its execution history so special care must be taken when making changes - /// to workflow code. For example, adding or removing activity tasks to a workflow's code may cause a - /// mismatch between code and history for in-flight workflows. To avoid potential issues related to workflow - /// versioning, consider applying the following code update strategies: - /// - /// - /// Deploy multiple versions of applications side-by-side allowing new code to run independently of old code. - /// - /// - /// Rather than changing existing workflows, create new workflows that implement the modified behavior. - /// - /// - /// Ensure all in-flight workflows are complete before applying code changes to existing workflow code. - /// - /// - /// If possible, only make changes to workflow code that won't impact its history or execution path. For - /// example, renaming variables or adding log statements have no impact on a workflow's execution path and - /// are safe to apply to existing workflows. - /// - /// - /// - /// - /// The type of the input parameter that this workflow accepts. This type must be JSON-serializable. - /// The type of the return value that this workflow produces. This type must be JSON-serializable. - public abstract class Workflow : IWorkflow - { - /// - Type IWorkflow.InputType => typeof(TInput); + /// The workflow's context. + /// The workflow's input. + /// Returns the workflow output as the result of a . + Task RunAsync(WorkflowContext context, object? input); +} - /// - Type IWorkflow.OutputType => typeof(TOutput); +/// +/// Represents the base class for workflows. +/// +/// +/// +/// Workflows describe how actions are executed and the order in which actions are executed. Workflows +/// don't call into external services or do complex computation directly. Rather, they delegate these tasks to +/// activities, which perform the actual work. +/// +/// +/// Workflows can be scheduled using the Dapr client or by other workflows as child-workflows using the +/// method. +/// +/// +/// Workflows may be replayed multiple times to rebuild their local state after being reloaded into memory. +/// workflow code must therefore be deterministic to ensure no unexpected side effects from execution +/// replay. To account for this behavior, there are several coding constraints to be aware of: +/// +/// +/// A workflow must not generate random numbers or random GUIDs, get the current date, read environment +/// variables, or do anything else that might result in a different value if the code is replayed in the future. +/// Activities and built-in properties and methods on the parameter, like +/// and , +/// can be used to work around these restrictions. +/// +/// +/// Workflow logic must be executed on the workflow thread (the thread that calls . +/// Creating new threads, scheduling callbacks on worker pool threads, or awaiting non-workflow tasks is forbidden +/// and may result in failures or other unexpected behavior. Blocking the workflow thread may also result in unexpected +/// performance degredation. The use of await should be restricted to workflow tasks - i.e. tasks returned from +/// methods on the parameter object or tasks that wrap these workflow tasks, like +/// and . +/// +/// +/// Avoid infinite loops as they could cause the application to run out of memory. Instead, ensure that loops are +/// bounded or use to restart the workflow with a new input. +/// +/// +/// Avoid logging normally in the workflow code because log messages will be duplicated on each replay. +/// Instead, write log statements when is false. +/// +/// +/// +/// +/// Workflow code is tightly coupled with its execution history so special care must be taken when making changes +/// to workflow code. For example, adding or removing activity tasks to a workflow's code may cause a +/// mismatch between code and history for in-flight workflows. To avoid potential issues related to workflow +/// versioning, consider applying the following code update strategies: +/// +/// +/// Deploy multiple versions of applications side-by-side allowing new code to run independently of old code. +/// +/// +/// Rather than changing existing workflows, create new workflows that implement the modified behavior. +/// +/// +/// Ensure all in-flight workflows are complete before applying code changes to existing workflow code. +/// +/// +/// If possible, only make changes to workflow code that won't impact its history or execution path. For +/// example, renaming variables or adding log statements have no impact on a workflow's execution path and +/// are safe to apply to existing workflows. +/// +/// +/// +/// +/// The type of the input parameter that this workflow accepts. This type must be JSON-serializable. +/// The type of the return value that this workflow produces. This type must be JSON-serializable. +public abstract class Workflow : IWorkflow +{ + /// + Type IWorkflow.InputType => typeof(TInput); - /// - async Task IWorkflow.RunAsync(WorkflowContext context, object? input) - { - return await this.RunAsync(context, (TInput)input!); - } + /// + Type IWorkflow.OutputType => typeof(TOutput); - /// - /// Override to implement workflow logic. - /// - /// The workflow context. - /// The deserialized workflow input. - /// The output of the workflow as a task. - public abstract Task RunAsync(WorkflowContext context, TInput input); + /// + async Task IWorkflow.RunAsync(WorkflowContext context, object? input) + { + return await this.RunAsync(context, (TInput)input!); } -} + + /// + /// Override to implement workflow logic. + /// + /// The workflow context. + /// The deserialized workflow input. + /// The output of the workflow as a task. + public abstract Task RunAsync(WorkflowContext context, TInput input); +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowActivity.cs b/src/Dapr.Workflow/WorkflowActivity.cs index 4daffe578..ff13de1c2 100644 --- a/src/Dapr.Workflow/WorkflowActivity.cs +++ b/src/Dapr.Workflow/WorkflowActivity.cs @@ -11,83 +11,82 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow -{ - using System; - using System.Threading.Tasks; +namespace Dapr.Workflow; + +using System; +using System.Threading.Tasks; +/// +/// Common interface for workflow activity implementations. +/// +/// +/// Users should not implement workflow activities using this interface, directly. +/// Instead, should be used to implement workflow activities. +/// +public interface IWorkflowActivity +{ /// - /// Common interface for workflow activity implementations. + /// Gets the type of the input parameter that this activity accepts. /// - /// - /// Users should not implement workflow activities using this interface, directly. - /// Instead, should be used to implement workflow activities. - /// - public interface IWorkflowActivity - { - /// - /// Gets the type of the input parameter that this activity accepts. - /// - Type InputType { get; } + Type InputType { get; } - /// - /// Gets the type of the return value that this activity produces. - /// - Type OutputType { get; } - - /// - /// Invokes the workflow activity with the specified context and input. - /// - /// The workflow activity's context. - /// The workflow activity's input. - /// Returns the workflow activity output as the result of a . - Task RunAsync(WorkflowActivityContext context, object? input); - } + /// + /// Gets the type of the return value that this activity produces. + /// + Type OutputType { get; } /// - /// Base class for workflow activities. + /// Invokes the workflow activity with the specified context and input. /// - /// - /// - /// Workflow activities are the basic unit of work in a workflow. Activities are the tasks that are - /// orchestrated in the business process. For example, you might create a workflow to process an order. The tasks - /// may involve checking the inventory, charging the customer, and creating a shipment. Each task would be a separate - /// activity. These activities may be executed serially, in parallel, or some combination of both. - /// - /// Unlike workflows, activities aren't restricted in the type of work you can do in them. Activities - /// are frequently used to make network calls or run CPU intensive operations. An activity can also return data back to - /// the workflow. The Dapr workflow engine guarantees that each called activity will be executed - /// at least once as part of a workflow's execution. - /// - /// Because activities only guarantee at least once execution, it's recommended that activity logic be implemented as - /// idempotent whenever possible. - /// - /// Activities are invoked by workflows using one of the - /// method overloads. - /// - /// - /// The type of the input parameter that this activity accepts. - /// The type of the return value that this activity produces. - public abstract class WorkflowActivity : IWorkflowActivity - { - /// - Type IWorkflowActivity.InputType => typeof(TInput); + /// The workflow activity's context. + /// The workflow activity's input. + /// Returns the workflow activity output as the result of a . + Task RunAsync(WorkflowActivityContext context, object? input); +} - /// - Type IWorkflowActivity.OutputType => typeof(TOutput); +/// +/// Base class for workflow activities. +/// +/// +/// +/// Workflow activities are the basic unit of work in a workflow. Activities are the tasks that are +/// orchestrated in the business process. For example, you might create a workflow to process an order. The tasks +/// may involve checking the inventory, charging the customer, and creating a shipment. Each task would be a separate +/// activity. These activities may be executed serially, in parallel, or some combination of both. +/// +/// Unlike workflows, activities aren't restricted in the type of work you can do in them. Activities +/// are frequently used to make network calls or run CPU intensive operations. An activity can also return data back to +/// the workflow. The Dapr workflow engine guarantees that each called activity will be executed +/// at least once as part of a workflow's execution. +/// +/// Because activities only guarantee at least once execution, it's recommended that activity logic be implemented as +/// idempotent whenever possible. +/// +/// Activities are invoked by workflows using one of the +/// method overloads. +/// +/// +/// The type of the input parameter that this activity accepts. +/// The type of the return value that this activity produces. +public abstract class WorkflowActivity : IWorkflowActivity +{ + /// + Type IWorkflowActivity.InputType => typeof(TInput); - /// - async Task IWorkflowActivity.RunAsync(WorkflowActivityContext context, object? input) - { - return await this.RunAsync(context, (TInput)input!); - } + /// + Type IWorkflowActivity.OutputType => typeof(TOutput); - /// - /// Override to implement async (non-blocking) workflow activity logic. - /// - /// Provides access to additional context for the current activity execution. - /// The deserialized activity input. - /// The output of the activity as a task. - public abstract Task RunAsync(WorkflowActivityContext context, TInput input); + /// + async Task IWorkflowActivity.RunAsync(WorkflowActivityContext context, object? input) + { + return await this.RunAsync(context, (TInput)input!); } -} + + /// + /// Override to implement async (non-blocking) workflow activity logic. + /// + /// Provides access to additional context for the current activity execution. + /// The deserialized activity input. + /// The output of the activity as a task. + public abstract Task RunAsync(WorkflowActivityContext context, TInput input); +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowActivityContext.cs b/src/Dapr.Workflow/WorkflowActivityContext.cs index a77c3ef91..ba3c3ee8b 100644 --- a/src/Dapr.Workflow/WorkflowActivityContext.cs +++ b/src/Dapr.Workflow/WorkflowActivityContext.cs @@ -11,23 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow -{ - using Microsoft.DurableTask; +namespace Dapr.Workflow; + +using Microsoft.DurableTask; +/// +/// Defines properties and methods for task activity context objects. +/// +public abstract class WorkflowActivityContext +{ /// - /// Defines properties and methods for task activity context objects. + /// Gets the name of the activity. /// - public abstract class WorkflowActivityContext - { - /// - /// Gets the name of the activity. - /// - public abstract TaskName Name { get; } + public abstract TaskName Name { get; } - /// - /// Gets the unique ID of the current workflow instance. - /// - public abstract string InstanceId { get; } - } -} + /// + /// Gets the unique ID of the current workflow instance. + /// + public abstract string InstanceId { get; } +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs index afc544ed5..4c25c9af8 100644 --- a/src/Dapr.Workflow/WorkflowContext.cs +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -13,325 +13,324 @@ using Microsoft.Extensions.Logging; -namespace Dapr.Workflow -{ - using System; - using System.Threading; - using System.Threading.Tasks; +namespace Dapr.Workflow; + +using System; +using System.Threading; +using System.Threading.Tasks; +/// +/// Context object used by workflow implementations to perform actions such as scheduling activities, durable timers, waiting for +/// external events, and for getting basic information about the current workflow instance. +/// +public abstract class WorkflowContext : IWorkflowContext +{ /// - /// Context object used by workflow implementations to perform actions such as scheduling activities, durable timers, waiting for - /// external events, and for getting basic information about the current workflow instance. + /// Gets the name of the current workflow. /// - public abstract class WorkflowContext : IWorkflowContext - { - /// - /// Gets the name of the current workflow. - /// - public abstract string Name { get; } + public abstract string Name { get; } - /// - /// Gets the instance ID of the current workflow. - /// - public abstract string InstanceId { get; } + /// + /// Gets the instance ID of the current workflow. + /// + public abstract string InstanceId { get; } - /// - /// Gets the current workflow time in UTC. - /// - /// - /// The current workflow time is stored in the workflow history and this API will - /// return the same value each time it is called from a particular point in the workflow's - /// execution. It is a deterministic, replay-safe replacement for existing .NET APIs for getting - /// the current time, such as and - /// (which should not be used). - /// - public abstract DateTime CurrentUtcDateTime { get; } + /// + /// Gets the current workflow time in UTC. + /// + /// + /// The current workflow time is stored in the workflow history and this API will + /// return the same value each time it is called from a particular point in the workflow's + /// execution. It is a deterministic, replay-safe replacement for existing .NET APIs for getting + /// the current time, such as and + /// (which should not be used). + /// + public abstract DateTime CurrentUtcDateTime { get; } - /// - /// Gets a value indicating whether the workflow is currently replaying a previous execution. - /// - /// - /// - /// Workflow functions are "replayed" after being unloaded from memory to reconstruct local variable state. - /// During a replay, previously executed tasks will be completed automatically with previously seen values - /// that are stored in the workflow history. One the workflow reaches the point in the workflow logic - /// where it's no longer replaying existing history, the property will return false. - /// - /// You can use this property if you have logic that needs to run only when not replaying. For example, - /// certain types of application logging may become too noisy when duplicated as part of replay. The - /// application code could check to see whether the function is being replayed and then issue the log statements - /// when this value is false. - /// - /// - /// - /// true if the workflow is currently replaying a previous execution; otherwise false. - /// - public abstract bool IsReplaying { get; } + /// + /// Gets a value indicating whether the workflow is currently replaying a previous execution. + /// + /// + /// + /// Workflow functions are "replayed" after being unloaded from memory to reconstruct local variable state. + /// During a replay, previously executed tasks will be completed automatically with previously seen values + /// that are stored in the workflow history. One the workflow reaches the point in the workflow logic + /// where it's no longer replaying existing history, the property will return false. + /// + /// You can use this property if you have logic that needs to run only when not replaying. For example, + /// certain types of application logging may become too noisy when duplicated as part of replay. The + /// application code could check to see whether the function is being replayed and then issue the log statements + /// when this value is false. + /// + /// + /// + /// true if the workflow is currently replaying a previous execution; otherwise false. + /// + public abstract bool IsReplaying { get; } - /// - /// Asynchronously invokes an activity by name and with the specified input value. - /// - /// - /// - /// Activities are the basic unit of work in a workflow. Unlike workflows, which are not - /// allowed to do any I/O or call non-deterministic APIs, activities have no implementation restrictions. - /// - /// An activity may execute in the local machine or a remote machine. The exact behavior depends on the underlying - /// workflow engine, which is responsible for distributing tasks across machines. In general, you should never make - /// any assumptions about where an activity will run. You should also assume at-least-once execution guarantees for - /// activities, meaning that an activity may be executed twice if, for example, there is a process failure before - /// the activities result is saved into storage. - /// - /// Both the inputs and outputs of activities are serialized and stored in durable storage. It's highly recommended - /// to not include any sensitive data in activity inputs or outputs. It's also recommended to not use large payloads - /// for activity inputs and outputs, which can result in expensive serialization and network utilization. For data - /// that cannot be cheaply or safely persisted to storage, it's recommended to instead pass references - /// (for example, a URL to a storage blob/bucket) to the data and have activities fetch the data directly as part of their - /// implementation. - /// - /// - /// The name of the activity to call. - /// The serializable input to pass to the activity. - /// Additional options that control the execution and processing of the activity. - /// A task that completes when the activity completes or fails. - /// The specified activity does not exist. - /// - /// Thrown if the calling thread is not the workflow dispatch thread. - /// - /// - /// The activity failed with an unhandled exception. The details of the failure can be found in the - /// property. - /// - public virtual Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null) - { - return this.CallActivityAsync(name, input, options); - } + /// + /// Asynchronously invokes an activity by name and with the specified input value. + /// + /// + /// + /// Activities are the basic unit of work in a workflow. Unlike workflows, which are not + /// allowed to do any I/O or call non-deterministic APIs, activities have no implementation restrictions. + /// + /// An activity may execute in the local machine or a remote machine. The exact behavior depends on the underlying + /// workflow engine, which is responsible for distributing tasks across machines. In general, you should never make + /// any assumptions about where an activity will run. You should also assume at-least-once execution guarantees for + /// activities, meaning that an activity may be executed twice if, for example, there is a process failure before + /// the activities result is saved into storage. + /// + /// Both the inputs and outputs of activities are serialized and stored in durable storage. It's highly recommended + /// to not include any sensitive data in activity inputs or outputs. It's also recommended to not use large payloads + /// for activity inputs and outputs, which can result in expensive serialization and network utilization. For data + /// that cannot be cheaply or safely persisted to storage, it's recommended to instead pass references + /// (for example, a URL to a storage blob/bucket) to the data and have activities fetch the data directly as part of their + /// implementation. + /// + /// + /// The name of the activity to call. + /// The serializable input to pass to the activity. + /// Additional options that control the execution and processing of the activity. + /// A task that completes when the activity completes or fails. + /// The specified activity does not exist. + /// + /// Thrown if the calling thread is not the workflow dispatch thread. + /// + /// + /// The activity failed with an unhandled exception. The details of the failure can be found in the + /// property. + /// + public virtual Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null) + { + return this.CallActivityAsync(name, input, options); + } - /// - /// A task that completes when the activity completes or fails. The result of the task is the activity's return value. - /// - /// - public abstract Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null); + /// + /// A task that completes when the activity completes or fails. The result of the task is the activity's return value. + /// + /// + public abstract Task CallActivityAsync(string name, object? input = null, WorkflowTaskOptions? options = null); - /// - /// Creates a durable timer that expires after the specified delay. - /// - /// The amount of time before the timer should expire. - /// Used to cancel the durable timer. - /// A task that completes when the durable timer expires. - /// - /// Thrown if the calling thread is not the workflow dispatch thread. - /// - public virtual Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) - { - return this.CreateTimer(this.CurrentUtcDateTime.Add(delay), cancellationToken); - } + /// + /// Creates a durable timer that expires after the specified delay. + /// + /// The amount of time before the timer should expire. + /// Used to cancel the durable timer. + /// A task that completes when the durable timer expires. + /// + /// Thrown if the calling thread is not the workflow dispatch thread. + /// + public virtual Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) + { + return this.CreateTimer(this.CurrentUtcDateTime.Add(delay), cancellationToken); + } - /// - /// Creates a durable timer that expires at a set date and time. - /// - /// The time at which the timer should expire. - /// Used to cancel the durable timer. - /// - public abstract Task CreateTimer(DateTime fireAt, CancellationToken cancellationToken); + /// + /// Creates a durable timer that expires at a set date and time. + /// + /// The time at which the timer should expire. + /// Used to cancel the durable timer. + /// + public abstract Task CreateTimer(DateTime fireAt, CancellationToken cancellationToken); - /// - /// Waits for an event to be raised with name and returns the event data. - /// - /// - /// - /// External clients can raise events to a waiting workflow instance. Similarly, workflows can raise - /// events to other workflows using the method. - /// - /// If the current workflow instance is not yet waiting for an event named , - /// then the event will be saved in the workflow instance state and dispatched immediately when this method is - /// called. This event saving occurs even if the current workflow cancels the wait operation before the event is - /// received. - /// - /// Workflows can wait for the same event name multiple times, so waiting for multiple events with the same name - /// is allowed. Each external event received by a workflow will complete just one task returned by this method. - /// - /// - /// - /// The name of the event to wait for. Event names are case-insensitive. External event names can be reused any - /// number of times; they are not required to be unique. - /// - /// A CancellationToken to use to abort waiting for the event. - /// Any serializable type that represents the event payload. - /// - /// A task that completes when the external event is received. The value of the task is the deserialized event payload. - /// - /// - /// Thrown if is cancelled before the external event is received. - /// - /// - /// Thrown if the calling thread is not the workflow dispatch thread. - /// - public abstract Task WaitForExternalEventAsync(string eventName, CancellationToken cancellationToken = default); + /// + /// Waits for an event to be raised with name and returns the event data. + /// + /// + /// + /// External clients can raise events to a waiting workflow instance. Similarly, workflows can raise + /// events to other workflows using the method. + /// + /// If the current workflow instance is not yet waiting for an event named , + /// then the event will be saved in the workflow instance state and dispatched immediately when this method is + /// called. This event saving occurs even if the current workflow cancels the wait operation before the event is + /// received. + /// + /// Workflows can wait for the same event name multiple times, so waiting for multiple events with the same name + /// is allowed. Each external event received by a workflow will complete just one task returned by this method. + /// + /// + /// + /// The name of the event to wait for. Event names are case-insensitive. External event names can be reused any + /// number of times; they are not required to be unique. + /// + /// A CancellationToken to use to abort waiting for the event. + /// Any serializable type that represents the event payload. + /// + /// A task that completes when the external event is received. The value of the task is the deserialized event payload. + /// + /// + /// Thrown if is cancelled before the external event is received. + /// + /// + /// Thrown if the calling thread is not the workflow dispatch thread. + /// + public abstract Task WaitForExternalEventAsync(string eventName, CancellationToken cancellationToken = default); - /// - /// Waits for an event to be raised with name and returns the event data. - /// - /// - /// The name of the event to wait for. Event names are case-insensitive. External event names can be reused any - /// number of times; they are not required to be unique. - /// - /// The amount of time to wait before cancelling the external event task. - /// - /// Thrown if elapses before the external event is received. - /// - /// - public abstract Task WaitForExternalEventAsync(string eventName, TimeSpan timeout); + /// + /// Waits for an event to be raised with name and returns the event data. + /// + /// + /// The name of the event to wait for. Event names are case-insensitive. External event names can be reused any + /// number of times; they are not required to be unique. + /// + /// The amount of time to wait before cancelling the external event task. + /// + /// Thrown if elapses before the external event is received. + /// + /// + public abstract Task WaitForExternalEventAsync(string eventName, TimeSpan timeout); - /// - /// Raises an external event for the specified workflow instance. - /// - /// - /// The target workflow can handle the sent event using the - /// method. - /// - /// If the target workflow doesn't exist, the event will be silently dropped. - /// - /// - /// The ID of the workflow instance to send the event to. - /// The name of the event to wait for. Event names are case-insensitive. - /// The serializable payload of the external event. - public abstract void SendEvent(string instanceId, string eventName, object payload); + /// + /// Raises an external event for the specified workflow instance. + /// + /// + /// The target workflow can handle the sent event using the + /// method. + /// + /// If the target workflow doesn't exist, the event will be silently dropped. + /// + /// + /// The ID of the workflow instance to send the event to. + /// The name of the event to wait for. Event names are case-insensitive. + /// The serializable payload of the external event. + public abstract void SendEvent(string instanceId, string eventName, object payload); - /// - /// Assigns a custom status value to the current workflow. - /// - /// - /// The value is serialized and stored in workflow state and will - /// be made available to the workflow status query APIs. - /// - /// - /// A serializable value to assign as the custom status value or null to clear the custom status. - /// - /// - /// Thrown if the calling thread is not the workflow dispatch thread. - /// - public abstract void SetCustomStatus(object? customStatus); + /// + /// Assigns a custom status value to the current workflow. + /// + /// + /// The value is serialized and stored in workflow state and will + /// be made available to the workflow status query APIs. + /// + /// + /// A serializable value to assign as the custom status value or null to clear the custom status. + /// + /// + /// Thrown if the calling thread is not the workflow dispatch thread. + /// + public abstract void SetCustomStatus(object? customStatus); - /// - /// Executes the specified workflow as a child workflow and returns the result. - /// - /// - /// The type into which to deserialize the child workflow's output. - /// - /// - public abstract Task CallChildWorkflowAsync( - string workflowName, - object? input = null, - ChildWorkflowTaskOptions? options = null); + /// + /// Executes the specified workflow as a child workflow and returns the result. + /// + /// + /// The type into which to deserialize the child workflow's output. + /// + /// + public abstract Task CallChildWorkflowAsync( + string workflowName, + object? input = null, + ChildWorkflowTaskOptions? options = null); - /// - /// Executes the specified workflow as a child workflow. - /// - /// - /// - /// In addition to activities, workflows can schedule other workflows as child workflows. - /// A child workflow has its own instance ID, history, and status that is independent of the parent workflow - /// that started it. You can use to specify an instance ID - /// for the child workflow. Otherwise, the instance ID will be randomly generated. - /// - /// Child workflows have many benefits: - /// - /// You can split large workflows into a series of smaller child workflows, making your code more maintainable. - /// You can distribute workflow logic across multiple compute nodes concurrently, which is useful if - /// your workflow logic otherwise needs to coordinate a lot of tasks. - /// You can reduce memory usage and CPU overhead by keeping the history of parent workflow smaller. - /// - /// - /// The return value of a child workflow is its output. If a child workflow fails with an exception, then that - /// exception will be surfaced to the parent workflow, just like it is when an activity task fails with an - /// exception. Child workflows also support automatic retry policies. - /// - /// Terminating a parent workflow terminates all the child workflows created by the workflow instance. See the documentation at - /// https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-features-concepts/#child-workflows regarding - /// the terminate workflow API for more information. - /// - /// - /// The name of the workflow to call. - /// The serializable input to pass to the child workflow. - /// - /// Additional options that control the execution and processing of the child workflow. - /// - /// A task that completes when the child workflow completes or fails. - /// The specified workflow does not exist. - /// - /// Thrown if the calling thread is not the workflow dispatch thread. - /// - /// - /// The child workflow failed with an unhandled exception. The details of the failure can be found in the - /// property. - /// - public virtual Task CallChildWorkflowAsync( - string workflowName, - object? input = null, - ChildWorkflowTaskOptions? options = null) - { - return this.CallChildWorkflowAsync(workflowName, input, options); - } + /// + /// Executes the specified workflow as a child workflow. + /// + /// + /// + /// In addition to activities, workflows can schedule other workflows as child workflows. + /// A child workflow has its own instance ID, history, and status that is independent of the parent workflow + /// that started it. You can use to specify an instance ID + /// for the child workflow. Otherwise, the instance ID will be randomly generated. + /// + /// Child workflows have many benefits: + /// + /// You can split large workflows into a series of smaller child workflows, making your code more maintainable. + /// You can distribute workflow logic across multiple compute nodes concurrently, which is useful if + /// your workflow logic otherwise needs to coordinate a lot of tasks. + /// You can reduce memory usage and CPU overhead by keeping the history of parent workflow smaller. + /// + /// + /// The return value of a child workflow is its output. If a child workflow fails with an exception, then that + /// exception will be surfaced to the parent workflow, just like it is when an activity task fails with an + /// exception. Child workflows also support automatic retry policies. + /// + /// Terminating a parent workflow terminates all the child workflows created by the workflow instance. See the documentation at + /// https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-features-concepts/#child-workflows regarding + /// the terminate workflow API for more information. + /// + /// + /// The name of the workflow to call. + /// The serializable input to pass to the child workflow. + /// + /// Additional options that control the execution and processing of the child workflow. + /// + /// A task that completes when the child workflow completes or fails. + /// The specified workflow does not exist. + /// + /// Thrown if the calling thread is not the workflow dispatch thread. + /// + /// + /// The child workflow failed with an unhandled exception. The details of the failure can be found in the + /// property. + /// + public virtual Task CallChildWorkflowAsync( + string workflowName, + object? input = null, + ChildWorkflowTaskOptions? options = null) + { + return this.CallChildWorkflowAsync(workflowName, input, options); + } - /// - /// Returns an instance of that is replay-safe, meaning that the logger only - /// writes logs when the orchestrator is not replaying previous history. - /// - /// The logger's category name. - /// An instance of that is replay-safe. - public abstract ILogger CreateReplaySafeLogger(string categoryName); + /// + /// Returns an instance of that is replay-safe, meaning that the logger only + /// writes logs when the orchestrator is not replaying previous history. + /// + /// The logger's category name. + /// An instance of that is replay-safe. + public abstract ILogger CreateReplaySafeLogger(string categoryName); - /// - /// The type to derive the category name from. - public abstract ILogger CreateReplaySafeLogger(Type type); + /// + /// The type to derive the category name from. + public abstract ILogger CreateReplaySafeLogger(Type type); - /// - /// The type to derive category name from. - public abstract ILogger CreateReplaySafeLogger(); + /// + /// The type to derive category name from. + public abstract ILogger CreateReplaySafeLogger(); - /// - /// Restarts the workflow with a new input and clears its history. - /// - /// - /// - /// This method is primarily designed for eternal workflows, which are workflows that - /// may not ever complete. It works by restarting the workflow, providing it with a new input, - /// and truncating the existing workflow history. It allows the workflow to continue - /// running indefinitely without having its history grow unbounded. The benefits of periodically - /// truncating history include decreased memory usage, decreased storage volumes, and shorter workflow - /// replays when rebuilding state. - /// - /// The results of any incomplete tasks will be discarded when a workflow calls - /// . For example, if a timer is scheduled and then - /// is called before the timer fires, the timer event will be discarded. The only exception to this - /// is external events. By default, if an external event is received by an workflow but not yet - /// processed, the event is saved in the workflow state unit it is received by a call to - /// . These events will continue to remain in memory - /// even after an workflow restarts using . You can disable this behavior and - /// remove any saved external events by specifying false for the - /// parameter value. - /// - /// Workflow implementations should complete immediately after calling the method. - /// - /// - /// The JSON-serializable input data to re-initialize the instance with. - /// - /// If set to true, re-adds any unprocessed external events into the new execution - /// history when the workflow instance restarts. If false, any unprocessed - /// external events will be discarded when the workflow instance restarts. - /// - public abstract void ContinueAsNew(object? newInput = null, bool preserveUnprocessedEvents = true); + /// + /// Restarts the workflow with a new input and clears its history. + /// + /// + /// + /// This method is primarily designed for eternal workflows, which are workflows that + /// may not ever complete. It works by restarting the workflow, providing it with a new input, + /// and truncating the existing workflow history. It allows the workflow to continue + /// running indefinitely without having its history grow unbounded. The benefits of periodically + /// truncating history include decreased memory usage, decreased storage volumes, and shorter workflow + /// replays when rebuilding state. + /// + /// The results of any incomplete tasks will be discarded when a workflow calls + /// . For example, if a timer is scheduled and then + /// is called before the timer fires, the timer event will be discarded. The only exception to this + /// is external events. By default, if an external event is received by an workflow but not yet + /// processed, the event is saved in the workflow state unit it is received by a call to + /// . These events will continue to remain in memory + /// even after an workflow restarts using . You can disable this behavior and + /// remove any saved external events by specifying false for the + /// parameter value. + /// + /// Workflow implementations should complete immediately after calling the method. + /// + /// + /// The JSON-serializable input data to re-initialize the instance with. + /// + /// If set to true, re-adds any unprocessed external events into the new execution + /// history when the workflow instance restarts. If false, any unprocessed + /// external events will be discarded when the workflow instance restarts. + /// + public abstract void ContinueAsNew(object? newInput = null, bool preserveUnprocessedEvents = true); - /// - /// Creates a new GUID that is safe for replay within a workflow. - /// - /// - /// The default implementation of this method creates a name-based UUID V5 using the algorithm from RFC 4122 §4.3. - /// The name input used to generate this value is a combination of the workflow instance ID, the current time, - /// and an internally managed sequence number. - /// - /// The new value. - public abstract Guid NewGuid(); - } -} + /// + /// Creates a new GUID that is safe for replay within a workflow. + /// + /// + /// The default implementation of this method creates a name-based UUID V5 using the algorithm from RFC 4122 §4.3. + /// The name input used to generate this value is a combination of the workflow instance ID, the current time, + /// and an internally managed sequence number. + /// + /// The new value. + public abstract Guid NewGuid(); +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowLoggingService.cs b/src/Dapr.Workflow/WorkflowLoggingService.cs index 115db817f..5b8521b29 100644 --- a/src/Dapr.Workflow/WorkflowLoggingService.cs +++ b/src/Dapr.Workflow/WorkflowLoggingService.cs @@ -11,63 +11,62 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow +namespace Dapr.Workflow; + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; + +/// +/// Defines runtime options for workflows. +/// +internal sealed class WorkflowLoggingService : IHostedService { - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Configuration; + private readonly ILogger logger; + private static readonly HashSet registeredWorkflows = new(); + private static readonly HashSet registeredActivities = new(); - /// - /// Defines runtime options for workflows. - /// - internal sealed class WorkflowLoggingService : IHostedService + public WorkflowLoggingService(ILogger logger) + { + this.logger = logger; + } + public Task StartAsync(CancellationToken cancellationToken) { - private readonly ILogger logger; - private static readonly HashSet registeredWorkflows = new(); - private static readonly HashSet registeredActivities = new(); + this.logger.Log(LogLevel.Information, "WorkflowLoggingService started"); - public WorkflowLoggingService(ILogger logger) - { - this.logger = logger; - } - public Task StartAsync(CancellationToken cancellationToken) + this.logger.Log(LogLevel.Information, "List of registered workflows"); + foreach (string item in registeredWorkflows) { - this.logger.Log(LogLevel.Information, "WorkflowLoggingService started"); - - this.logger.Log(LogLevel.Information, "List of registered workflows"); - foreach (string item in registeredWorkflows) - { - this.logger.Log(LogLevel.Information, item); - } - - this.logger.Log(LogLevel.Information, "List of registered activities:"); - foreach (string item in registeredActivities) - { - this.logger.Log(LogLevel.Information, item); - } - - return Task.CompletedTask; + this.logger.Log(LogLevel.Information, item); } - public Task StopAsync(CancellationToken cancellationToken) + this.logger.Log(LogLevel.Information, "List of registered activities:"); + foreach (string item in registeredActivities) { - this.logger.Log(LogLevel.Information, "WorkflowLoggingService stopped"); - - return Task.CompletedTask; + this.logger.Log(LogLevel.Information, item); } - public static void LogWorkflowName(string workflowName) - { - registeredWorkflows.Add(workflowName); - } + return Task.CompletedTask; + } - public static void LogActivityName(string activityName) - { - registeredActivities.Add(activityName); - } + public Task StopAsync(CancellationToken cancellationToken) + { + this.logger.Log(LogLevel.Information, "WorkflowLoggingService stopped"); + + return Task.CompletedTask; + } + public static void LogWorkflowName(string workflowName) + { + registeredWorkflows.Add(workflowName); } -} + + public static void LogActivityName(string activityName) + { + registeredActivities.Add(activityName); + } + +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowRetryPolicy.cs b/src/Dapr.Workflow/WorkflowRetryPolicy.cs index 35f0f9108..d20fe20a0 100644 --- a/src/Dapr.Workflow/WorkflowRetryPolicy.cs +++ b/src/Dapr.Workflow/WorkflowRetryPolicy.cs @@ -14,91 +14,90 @@ using System.Threading; using Microsoft.DurableTask; -namespace Dapr.Workflow +namespace Dapr.Workflow; + +/// +/// A declarative retry policy that can be configured for activity or child workflow calls. +/// +public class WorkflowRetryPolicy { + readonly RetryPolicy durableRetryPolicy; + /// - /// A declarative retry policy that can be configured for activity or child workflow calls. + /// Initializes a new instance of the class. /// - public class WorkflowRetryPolicy + /// The maximum number of task invocation attempts. Must be 1 or greater. + /// The amount of time to delay between the first and second attempt. + /// + /// The exponential back-off coefficient used to determine the delay between subsequent retries. Must be 1.0 or greater. + /// + /// + /// The maximum time to delay between attempts, regardless of. + /// + /// The overall timeout for retries. + /// + /// The value can be used to specify an unlimited timeout for + /// or . + /// + /// + /// Thrown if any of the following are true: + /// + /// The value for is less than or equal to zero. + /// The value for is less than or equal to . + /// The value for is less than 1.0. + /// The value for is less than . + /// The value for is less than . + /// + /// + public WorkflowRetryPolicy( + int maxNumberOfAttempts, + TimeSpan firstRetryInterval, + double backoffCoefficient = 1.0, + TimeSpan? maxRetryInterval = null, + TimeSpan? retryTimeout = null) { - readonly RetryPolicy durableRetryPolicy; - - /// - /// Initializes a new instance of the class. - /// - /// The maximum number of task invocation attempts. Must be 1 or greater. - /// The amount of time to delay between the first and second attempt. - /// - /// The exponential back-off coefficient used to determine the delay between subsequent retries. Must be 1.0 or greater. - /// - /// - /// The maximum time to delay between attempts, regardless of. - /// - /// The overall timeout for retries. - /// - /// The value can be used to specify an unlimited timeout for - /// or . - /// - /// - /// Thrown if any of the following are true: - /// - /// The value for is less than or equal to zero. - /// The value for is less than or equal to . - /// The value for is less than 1.0. - /// The value for is less than . - /// The value for is less than . - /// - /// - public WorkflowRetryPolicy( - int maxNumberOfAttempts, - TimeSpan firstRetryInterval, - double backoffCoefficient = 1.0, - TimeSpan? maxRetryInterval = null, - TimeSpan? retryTimeout = null) - { - this.durableRetryPolicy = new RetryPolicy( - maxNumberOfAttempts, - firstRetryInterval, - backoffCoefficient, - maxRetryInterval, - retryTimeout); - } + this.durableRetryPolicy = new RetryPolicy( + maxNumberOfAttempts, + firstRetryInterval, + backoffCoefficient, + maxRetryInterval, + retryTimeout); + } - /// - /// Gets the max number of attempts for executing a given task. - /// - public int MaxNumberOfAttempts => this.durableRetryPolicy.MaxNumberOfAttempts; + /// + /// Gets the max number of attempts for executing a given task. + /// + public int MaxNumberOfAttempts => this.durableRetryPolicy.MaxNumberOfAttempts; - /// - /// Gets the amount of time to delay between the first and second attempt. - /// - public TimeSpan FirstRetryInterval => this.durableRetryPolicy.FirstRetryInterval; + /// + /// Gets the amount of time to delay between the first and second attempt. + /// + public TimeSpan FirstRetryInterval => this.durableRetryPolicy.FirstRetryInterval; - /// - /// Gets the exponential back-off coefficient used to determine the delay between subsequent retries. - /// - /// - /// Defaults to 1.0 for no back-off. - /// - public double BackoffCoefficient => this.durableRetryPolicy.BackoffCoefficient; + /// + /// Gets the exponential back-off coefficient used to determine the delay between subsequent retries. + /// + /// + /// Defaults to 1.0 for no back-off. + /// + public double BackoffCoefficient => this.durableRetryPolicy.BackoffCoefficient; - /// - /// Gets the maximum time to delay between attempts. - /// - /// - /// Defaults to 1 hour. - /// - public TimeSpan MaxRetryInterval => this.durableRetryPolicy.MaxRetryInterval; + /// + /// Gets the maximum time to delay between attempts. + /// + /// + /// Defaults to 1 hour. + /// + public TimeSpan MaxRetryInterval => this.durableRetryPolicy.MaxRetryInterval; - /// - /// Gets the overall timeout for retries. No further attempts will be made at executing a task after this retry - /// timeout expires. - /// - /// - /// Defaults to . - /// - public TimeSpan RetryTimeout => this.durableRetryPolicy.RetryTimeout; + /// + /// Gets the overall timeout for retries. No further attempts will be made at executing a task after this retry + /// timeout expires. + /// + /// + /// Defaults to . + /// + public TimeSpan RetryTimeout => this.durableRetryPolicy.RetryTimeout; - internal RetryPolicy GetDurableRetryPolicy() => this.durableRetryPolicy; - } -} + internal RetryPolicy GetDurableRetryPolicy() => this.durableRetryPolicy; +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 9afdfb5e7..a7906db2e 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -13,179 +13,177 @@ using Grpc.Net.Client; -namespace Dapr.Workflow +namespace Dapr.Workflow; + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.DurableTask; +using Microsoft.Extensions.DependencyInjection; + +/// +/// Defines runtime options for workflows. +/// +public sealed class WorkflowRuntimeOptions { - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using Microsoft.DurableTask; - using Microsoft.Extensions.DependencyInjection; + /// + /// Dictionary to name and register a workflow. + /// + readonly Dictionary> factories = new(); /// - /// Defines runtime options for workflows. + /// Override GrpcChannelOptions. /// - public sealed class WorkflowRuntimeOptions - { - /// - /// Dictionary to name and register a workflow. - /// - readonly Dictionary> factories = new(); - - /// - /// Override GrpcChannelOptions. - /// - internal GrpcChannelOptions? GrpcChannelOptions { get; private set; } + internal GrpcChannelOptions? GrpcChannelOptions { get; private set; } - /// - /// Initializes a new instance of the class. - /// - /// - /// Instances of this type are expected to be instanciated from a dependency injection container. - /// - public WorkflowRuntimeOptions() - { - } + /// + /// Initializes a new instance of the class. + /// + /// + /// Instances of this type are expected to be instanciated from a dependency injection container. + /// + public WorkflowRuntimeOptions() + { + } - /// - /// Registers a workflow as a function that takes a specified input type and returns a specified output type. - /// - /// Workflow name - /// Function implementing the workflow definition - public void RegisterWorkflow(string name, Func> implementation) + /// + /// Registers a workflow as a function that takes a specified input type and returns a specified output type. + /// + /// Workflow name + /// Function implementing the workflow definition + public void RegisterWorkflow(string name, Func> implementation) + { + // Dapr workflows are implemented as specialized Durable Task orchestrations + this.factories.Add(name, (DurableTaskRegistry registry) => { - // Dapr workflows are implemented as specialized Durable Task orchestrations - this.factories.Add(name, (DurableTaskRegistry registry) => + registry.AddOrchestratorFunc(name, (innerContext, input) => { - registry.AddOrchestratorFunc(name, (innerContext, input) => - { - WorkflowContext workflowContext = new DaprWorkflowContext(innerContext); - return implementation(workflowContext, input); - }); - WorkflowLoggingService.LogWorkflowName(name); + WorkflowContext workflowContext = new DaprWorkflowContext(innerContext); + return implementation(workflowContext, input); }); - } + WorkflowLoggingService.LogWorkflowName(name); + }); + } - /// - /// Registers a workflow class that derives from . - /// - /// The type to register. - public void RegisterWorkflow() where TWorkflow : class, IWorkflow, new() - { - string name = typeof(TWorkflow).Name; + /// + /// Registers a workflow class that derives from . + /// + /// The type to register. + public void RegisterWorkflow() where TWorkflow : class, IWorkflow, new() + { + string name = typeof(TWorkflow).Name; - // Dapr workflows are implemented as specialized Durable Task orchestrations - this.factories.Add(name, (DurableTaskRegistry registry) => + // Dapr workflows are implemented as specialized Durable Task orchestrations + this.factories.Add(name, (DurableTaskRegistry registry) => + { + registry.AddOrchestrator(name, () => { - registry.AddOrchestrator(name, () => - { - TWorkflow workflow = Activator.CreateInstance(); - return new OrchestratorWrapper(workflow); - }); - WorkflowLoggingService.LogWorkflowName(name); + TWorkflow workflow = Activator.CreateInstance(); + return new OrchestratorWrapper(workflow); }); - } + WorkflowLoggingService.LogWorkflowName(name); + }); + } - /// - /// Registers a workflow activity as a function that takes a specified input type and returns a specified output type. - /// - /// Activity name - /// Activity implemetation - public void RegisterActivity(string name, Func> implementation) + /// + /// Registers a workflow activity as a function that takes a specified input type and returns a specified output type. + /// + /// Activity name + /// Activity implemetation + public void RegisterActivity(string name, Func> implementation) + { + // Dapr activities are implemented as specialized Durable Task activities + this.factories.Add(name, (DurableTaskRegistry registry) => { - // Dapr activities are implemented as specialized Durable Task activities - this.factories.Add(name, (DurableTaskRegistry registry) => + registry.AddActivityFunc(name, (innerContext, input) => { - registry.AddActivityFunc(name, (innerContext, input) => - { - WorkflowActivityContext activityContext = new DaprWorkflowActivityContext(innerContext); - return implementation(activityContext, input); - }); - WorkflowLoggingService.LogActivityName(name); + WorkflowActivityContext activityContext = new DaprWorkflowActivityContext(innerContext); + return implementation(activityContext, input); }); - } + WorkflowLoggingService.LogActivityName(name); + }); + } - /// - /// Registers a workflow activity class that derives from . - /// - /// The type to register. - public void RegisterActivity() where TActivity : class, IWorkflowActivity - { - string name = typeof(TActivity).Name; + /// + /// Registers a workflow activity class that derives from . + /// + /// The type to register. + public void RegisterActivity() where TActivity : class, IWorkflowActivity + { + string name = typeof(TActivity).Name; - // Dapr workflows are implemented as specialized Durable Task orchestrations - this.factories.Add(name, (DurableTaskRegistry registry) => + // Dapr workflows are implemented as specialized Durable Task orchestrations + this.factories.Add(name, (DurableTaskRegistry registry) => + { + registry.AddActivity(name, serviceProvider => { - registry.AddActivity(name, serviceProvider => - { - // Workflow activity classes support dependency injection. - TActivity activity = ActivatorUtilities.CreateInstance(serviceProvider); - return new ActivityWrapper(activity); - }); - WorkflowLoggingService.LogActivityName(name); + // Workflow activity classes support dependency injection. + TActivity activity = ActivatorUtilities.CreateInstance(serviceProvider); + return new ActivityWrapper(activity); }); - } + WorkflowLoggingService.LogActivityName(name); + }); + } - /// - /// Uses the provided for creating the . - /// - /// The to use for creating the . - public void UseGrpcChannelOptions(GrpcChannelOptions grpcChannelOptions) - { - this.GrpcChannelOptions = grpcChannelOptions; - } + /// + /// Uses the provided for creating the . + /// + /// The to use for creating the . + public void UseGrpcChannelOptions(GrpcChannelOptions grpcChannelOptions) + { + this.GrpcChannelOptions = grpcChannelOptions; + } - /// - /// Method to add workflows and activities to the registry. - /// - /// The registry we will add workflows and activities to - internal void AddWorkflowsAndActivitiesToRegistry(DurableTaskRegistry registry) + /// + /// Method to add workflows and activities to the registry. + /// + /// The registry we will add workflows and activities to + internal void AddWorkflowsAndActivitiesToRegistry(DurableTaskRegistry registry) + { + foreach (Action factory in this.factories.Values) { - foreach (Action factory in this.factories.Values) - { - factory.Invoke(registry); // This adds workflows to the registry indirectly. - } + factory.Invoke(registry); // This adds workflows to the registry indirectly. } + } - /// - /// Helper class that provides a Durable Task orchestrator wrapper for a workflow. - /// - class OrchestratorWrapper : ITaskOrchestrator - { - readonly IWorkflow workflow; + /// + /// Helper class that provides a Durable Task orchestrator wrapper for a workflow. + /// + class OrchestratorWrapper : ITaskOrchestrator + { + readonly IWorkflow workflow; - public OrchestratorWrapper(IWorkflow workflow) - { - this.workflow = workflow; - } + public OrchestratorWrapper(IWorkflow workflow) + { + this.workflow = workflow; + } - public Type InputType => this.workflow.InputType; + public Type InputType => this.workflow.InputType; - public Type OutputType => this.workflow.OutputType; + public Type OutputType => this.workflow.OutputType; - public Task RunAsync(TaskOrchestrationContext context, object? input) - { - return this.workflow.RunAsync(new DaprWorkflowContext(context), input); - } + public Task RunAsync(TaskOrchestrationContext context, object? input) + { + return this.workflow.RunAsync(new DaprWorkflowContext(context), input); } + } - class ActivityWrapper : ITaskActivity - { - readonly IWorkflowActivity activity; + class ActivityWrapper : ITaskActivity + { + readonly IWorkflowActivity activity; - public ActivityWrapper(IWorkflowActivity activity) - { - this.activity = activity; - } + public ActivityWrapper(IWorkflowActivity activity) + { + this.activity = activity; + } - public Type InputType => this.activity.InputType; + public Type InputType => this.activity.InputType; - public Type OutputType => this.activity.OutputType; + public Type OutputType => this.activity.OutputType; - public Task RunAsync(TaskActivityContext context, object? input) - { - return this.activity.RunAsync(new DaprWorkflowActivityContext(context), input); - } + public Task RunAsync(TaskActivityContext context, object? input) + { + return this.activity.RunAsync(new DaprWorkflowActivityContext(context), input); } } -} - +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowRuntimeStatus.cs b/src/Dapr.Workflow/WorkflowRuntimeStatus.cs index 24024cd63..9cc0942f8 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeStatus.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeStatus.cs @@ -11,46 +11,45 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow +namespace Dapr.Workflow; + +/// +/// Enum describing the runtime status of a workflow. +/// +public enum WorkflowRuntimeStatus { /// - /// Enum describing the runtime status of a workflow. + /// The status of the workflow is unknown. + /// + Unknown = -1, + + /// + /// The workflow started running. + /// + Running, + + /// + /// The workflow completed normally. + /// + Completed, + + /// + /// The workflow completed with an unhandled exception. + /// + Failed, + + /// + /// The workflow was abruptly terminated via a management API call. + /// + Terminated, + + /// + /// The workflow was scheduled but hasn't started running. + /// + Pending, + + /// + /// The workflow was suspended. /// - public enum WorkflowRuntimeStatus - { - /// - /// The status of the workflow is unknown. - /// - Unknown = -1, - - /// - /// The workflow started running. - /// - Running, - - /// - /// The workflow completed normally. - /// - Completed, - - /// - /// The workflow completed with an unhandled exception. - /// - Failed, - - /// - /// The workflow was abruptly terminated via a management API call. - /// - Terminated, - - /// - /// The workflow was scheduled but hasn't started running. - /// - Pending, - - /// - /// The workflow was suspended. - /// - Suspended, - } -} + Suspended, +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowState.cs b/src/Dapr.Workflow/WorkflowState.cs index ea1ffae22..caaba0f9e 100644 --- a/src/Dapr.Workflow/WorkflowState.cs +++ b/src/Dapr.Workflow/WorkflowState.cs @@ -11,155 +11,154 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow +namespace Dapr.Workflow; + +using System; +using Dapr.Client; +using Microsoft.DurableTask.Client; + +/// +/// Represents a snapshot of a workflow instance's current state, including runtime status. +/// +public class WorkflowState { - using System; - using Dapr.Client; - using Microsoft.DurableTask.Client; + readonly OrchestrationMetadata? workflowState; + readonly WorkflowTaskFailureDetails? failureDetails; - /// - /// Represents a snapshot of a workflow instance's current state, including runtime status. - /// - public class WorkflowState + internal WorkflowState(OrchestrationMetadata? orchestrationMetadata) { - readonly OrchestrationMetadata? workflowState; - readonly WorkflowTaskFailureDetails? failureDetails; - - internal WorkflowState(OrchestrationMetadata? orchestrationMetadata) + // This value will be null if the workflow doesn't exist. + this.workflowState = orchestrationMetadata; + if (orchestrationMetadata?.FailureDetails != null) { - // This value will be null if the workflow doesn't exist. - this.workflowState = orchestrationMetadata; - if (orchestrationMetadata?.FailureDetails != null) - { - this.failureDetails = new WorkflowTaskFailureDetails(orchestrationMetadata.FailureDetails); - } + this.failureDetails = new WorkflowTaskFailureDetails(orchestrationMetadata.FailureDetails); } + } - /// - /// Gets a value indicating whether the requested workflow instance exists. - /// - public bool Exists => this.workflowState != null; - - /// - /// Gets a value indicating whether the requested workflow is in a running state. - /// - public bool IsWorkflowRunning => this.workflowState?.RuntimeStatus == OrchestrationRuntimeStatus.Running; - - /// - /// Gets a value indicating whether the requested workflow is in a terminal state. - /// - public bool IsWorkflowCompleted => this.workflowState?.IsCompleted == true; - - /// - /// Gets the time at which this workflow instance was created. - /// - public DateTimeOffset CreatedAt => this.workflowState?.CreatedAt ?? default; - - /// - /// Gets the time at which this workflow instance last had its state updated. - /// - public DateTimeOffset LastUpdatedAt => this.workflowState?.LastUpdatedAt ?? default; - - /// - /// Gets the execution status of the workflow. - /// - public WorkflowRuntimeStatus RuntimeStatus - { - get - { - if (this.workflowState == null) - { - return WorkflowRuntimeStatus.Unknown; - } - - switch (this.workflowState.RuntimeStatus) - { - case OrchestrationRuntimeStatus.Running: - return WorkflowRuntimeStatus.Running; - case OrchestrationRuntimeStatus.Completed: - return WorkflowRuntimeStatus.Completed; - case OrchestrationRuntimeStatus.Failed: - return WorkflowRuntimeStatus.Failed; - case OrchestrationRuntimeStatus.Terminated: - return WorkflowRuntimeStatus.Terminated; - case OrchestrationRuntimeStatus.Pending: - return WorkflowRuntimeStatus.Pending; - case OrchestrationRuntimeStatus.Suspended: - return WorkflowRuntimeStatus.Suspended; - default: - return WorkflowRuntimeStatus.Unknown; - } - } - } + /// + /// Gets a value indicating whether the requested workflow instance exists. + /// + public bool Exists => this.workflowState != null; - /// - /// Gets the failure details, if any, for the workflow instance. - /// - /// - /// This property contains data only if the workflow is in the - /// state, and only if this instance metadata was fetched with the option to include output data. - /// - /// The failure details if the workflow was in a failed state; null otherwise. - public WorkflowTaskFailureDetails? FailureDetails => this.failureDetails; - - /// - /// Deserializes the workflow input into . - /// - /// The type to deserialize the workflow input into. - /// Returns the input as , or returns a default value if the workflow doesn't exist. - public T? ReadInputAs() + /// + /// Gets a value indicating whether the requested workflow is in a running state. + /// + public bool IsWorkflowRunning => this.workflowState?.RuntimeStatus == OrchestrationRuntimeStatus.Running; + + /// + /// Gets a value indicating whether the requested workflow is in a terminal state. + /// + public bool IsWorkflowCompleted => this.workflowState?.IsCompleted == true; + + /// + /// Gets the time at which this workflow instance was created. + /// + public DateTimeOffset CreatedAt => this.workflowState?.CreatedAt ?? default; + + /// + /// Gets the time at which this workflow instance last had its state updated. + /// + public DateTimeOffset LastUpdatedAt => this.workflowState?.LastUpdatedAt ?? default; + + /// + /// Gets the execution status of the workflow. + /// + public WorkflowRuntimeStatus RuntimeStatus + { + get { if (this.workflowState == null) { - return default; + return WorkflowRuntimeStatus.Unknown; } - if (string.IsNullOrEmpty(this.workflowState.SerializedInput)) + switch (this.workflowState.RuntimeStatus) { - return default; + case OrchestrationRuntimeStatus.Running: + return WorkflowRuntimeStatus.Running; + case OrchestrationRuntimeStatus.Completed: + return WorkflowRuntimeStatus.Completed; + case OrchestrationRuntimeStatus.Failed: + return WorkflowRuntimeStatus.Failed; + case OrchestrationRuntimeStatus.Terminated: + return WorkflowRuntimeStatus.Terminated; + case OrchestrationRuntimeStatus.Pending: + return WorkflowRuntimeStatus.Pending; + case OrchestrationRuntimeStatus.Suspended: + return WorkflowRuntimeStatus.Suspended; + default: + return WorkflowRuntimeStatus.Unknown; } + } + } - return this.workflowState.ReadInputAs(); + /// + /// Gets the failure details, if any, for the workflow instance. + /// + /// + /// This property contains data only if the workflow is in the + /// state, and only if this instance metadata was fetched with the option to include output data. + /// + /// The failure details if the workflow was in a failed state; null otherwise. + public WorkflowTaskFailureDetails? FailureDetails => this.failureDetails; + + /// + /// Deserializes the workflow input into . + /// + /// The type to deserialize the workflow input into. + /// Returns the input as , or returns a default value if the workflow doesn't exist. + public T? ReadInputAs() + { + if (this.workflowState == null) + { + return default; } - /// - /// Deserializes the workflow output into . - /// - /// The type to deserialize the workflow output into. - /// Returns the output as , or returns a default value if the workflow doesn't exist. - public T? ReadOutputAs() + if (string.IsNullOrEmpty(this.workflowState.SerializedInput)) { - if (this.workflowState == null) - { - return default; - } + return default; + } - if (string.IsNullOrEmpty(this.workflowState.SerializedOutput)) - { - return default; - } + return this.workflowState.ReadInputAs(); + } - return this.workflowState.ReadOutputAs(); + /// + /// Deserializes the workflow output into . + /// + /// The type to deserialize the workflow output into. + /// Returns the output as , or returns a default value if the workflow doesn't exist. + public T? ReadOutputAs() + { + if (this.workflowState == null) + { + return default; } - /// - /// Deserializes the workflow's custom status into . - /// - /// The type to deserialize the workflow's custom status into. - /// Returns the custom status as , or returns a default value if the workflow doesn't exist. - public T? ReadCustomStatusAs() + if (string.IsNullOrEmpty(this.workflowState.SerializedOutput)) { - if (this.workflowState == null) - { - return default; - } + return default; + } - if (string.IsNullOrEmpty(this.workflowState.SerializedCustomStatus)) - { - return default; - } + return this.workflowState.ReadOutputAs(); + } - return this.workflowState.ReadCustomStatusAs(); + /// + /// Deserializes the workflow's custom status into . + /// + /// The type to deserialize the workflow's custom status into. + /// Returns the custom status as , or returns a default value if the workflow doesn't exist. + public T? ReadCustomStatusAs() + { + if (this.workflowState == null) + { + return default; } + + if (string.IsNullOrEmpty(this.workflowState.SerializedCustomStatus)) + { + return default; + } + + return this.workflowState.ReadCustomStatusAs(); } -} +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowTaskFailedException.cs b/src/Dapr.Workflow/WorkflowTaskFailedException.cs index 575947a3a..331793fee 100644 --- a/src/Dapr.Workflow/WorkflowTaskFailedException.cs +++ b/src/Dapr.Workflow/WorkflowTaskFailedException.cs @@ -11,29 +11,28 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow -{ - using System; +namespace Dapr.Workflow; + +using System; +/// +/// Exception type for Dapr Workflow task failures. +/// +public class WorkflowTaskFailedException : Exception +{ /// - /// Exception type for Dapr Workflow task failures. + /// Initializes a new instance of the class. /// - public class WorkflowTaskFailedException : Exception + /// The exception message. + /// Details about the failure. + public WorkflowTaskFailedException(string message, WorkflowTaskFailureDetails failureDetails) + : base(message) { - /// - /// Initializes a new instance of the class. - /// - /// The exception message. - /// Details about the failure. - public WorkflowTaskFailedException(string message, WorkflowTaskFailureDetails failureDetails) - : base(message) - { - this.FailureDetails = failureDetails ?? throw new ArgumentNullException(nameof(failureDetails)); - } - - /// - /// Gets more information about the underlying workflow task failure. - /// - public WorkflowTaskFailureDetails FailureDetails { get; } + this.FailureDetails = failureDetails ?? throw new ArgumentNullException(nameof(failureDetails)); } -} + + /// + /// Gets more information about the underlying workflow task failure. + /// + public WorkflowTaskFailureDetails FailureDetails { get; } +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowTaskFailureDetails.cs b/src/Dapr.Workflow/WorkflowTaskFailureDetails.cs index 167ba2102..35733b22c 100644 --- a/src/Dapr.Workflow/WorkflowTaskFailureDetails.cs +++ b/src/Dapr.Workflow/WorkflowTaskFailureDetails.cs @@ -11,62 +11,61 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow +namespace Dapr.Workflow; + +using System; +using Microsoft.DurableTask; + +/// +/// Represents workflow task failure details. +/// +public class WorkflowTaskFailureDetails { - using System; - using Microsoft.DurableTask; + readonly TaskFailureDetails details; - /// - /// Represents workflow task failure details. - /// - public class WorkflowTaskFailureDetails + internal WorkflowTaskFailureDetails(TaskFailureDetails details) { - readonly TaskFailureDetails details; - - internal WorkflowTaskFailureDetails(TaskFailureDetails details) - { - this.details = details ?? throw new ArgumentNullException(nameof(details)); - } + this.details = details ?? throw new ArgumentNullException(nameof(details)); + } - /// - /// Gets the error type, which is the namespace-qualified exception type name. - /// - public string ErrorType => this.details.ErrorType; + /// + /// Gets the error type, which is the namespace-qualified exception type name. + /// + public string ErrorType => this.details.ErrorType; - /// - /// Gets a summary description of the failure, which is typically an exception message. - /// - public string ErrorMessage => this.details.ErrorMessage; + /// + /// Gets a summary description of the failure, which is typically an exception message. + /// + public string ErrorMessage => this.details.ErrorMessage; - /// - /// Gets the stack trace of the failure. - /// - public string? StackTrace => this.details.StackTrace; + /// + /// Gets the stack trace of the failure. + /// + public string? StackTrace => this.details.StackTrace; - /// - /// Returns true if the failure was caused by the specified exception type. - /// - /// - /// This method allows checking if a workflow task failed due to an exception of a specific type by attempting - /// to load the type specified in . If the exception type cannot be loaded - /// for any reason, this method will return false. Base types are supported. - /// - /// The type of exception to test against. - /// - /// Returns true if the value matches ; false otherwise. - /// - public bool IsCausedBy() where T : Exception - { - return this.details.IsCausedBy(); - } + /// + /// Returns true if the failure was caused by the specified exception type. + /// + /// + /// This method allows checking if a workflow task failed due to an exception of a specific type by attempting + /// to load the type specified in . If the exception type cannot be loaded + /// for any reason, this method will return false. Base types are supported. + /// + /// The type of exception to test against. + /// + /// Returns true if the value matches ; false otherwise. + /// + public bool IsCausedBy() where T : Exception + { + return this.details.IsCausedBy(); + } - /// - /// Gets a debug-friendly description of the failure information. - /// - /// A debugger friendly display string. - public override string ToString() - { - return $"{this.ErrorType}: {this.ErrorMessage}"; - } + /// + /// Gets a debug-friendly description of the failure information. + /// + /// A debugger friendly display string. + public override string ToString() + { + return $"{this.ErrorType}: {this.ErrorMessage}"; } -} +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowTaskOptions.cs b/src/Dapr.Workflow/WorkflowTaskOptions.cs index d93dbc551..b020f23a2 100644 --- a/src/Dapr.Workflow/WorkflowTaskOptions.cs +++ b/src/Dapr.Workflow/WorkflowTaskOptions.cs @@ -13,56 +13,55 @@ using Microsoft.DurableTask; -namespace Dapr.Workflow +namespace Dapr.Workflow; + +/// +/// Options that can be used to control the behavior of workflow task execution. +/// +/// The workflow retry policy. +public record WorkflowTaskOptions(WorkflowRetryPolicy? RetryPolicy = null) { - /// - /// Options that can be used to control the behavior of workflow task execution. - /// - /// The workflow retry policy. - public record WorkflowTaskOptions(WorkflowRetryPolicy? RetryPolicy = null) + internal TaskOptions ToDurableTaskOptions() { - internal TaskOptions ToDurableTaskOptions() + TaskRetryOptions? retryOptions = null; + if (this.RetryPolicy is not null) { - TaskRetryOptions? retryOptions = null; - if (this.RetryPolicy is not null) - { - retryOptions = this.RetryPolicy.GetDurableRetryPolicy(); - } - - return new TaskOptions(retryOptions); + retryOptions = this.RetryPolicy.GetDurableRetryPolicy(); } + + return new TaskOptions(retryOptions); } +} +/// +/// Options for controlling the behavior of child workflow execution. +/// +public record ChildWorkflowTaskOptions : WorkflowTaskOptions +{ /// - /// Options for controlling the behavior of child workflow execution. + /// Initializes a new instance of the record. /// - public record ChildWorkflowTaskOptions : WorkflowTaskOptions + /// The instance ID to use for the child workflow. + /// The child workflow's retry policy. + public ChildWorkflowTaskOptions(string? instanceId = null, WorkflowRetryPolicy ? retryPolicy = null) + : base(retryPolicy) { - /// - /// Initializes a new instance of the record. - /// - /// The instance ID to use for the child workflow. - /// The child workflow's retry policy. - public ChildWorkflowTaskOptions(string? instanceId = null, WorkflowRetryPolicy ? retryPolicy = null) - : base(retryPolicy) - { - this.InstanceId = instanceId; - } + this.InstanceId = instanceId; + } - /// - /// Gets the instance ID to use when creating a child workflow. - /// - public string? InstanceId { get; init; } + /// + /// Gets the instance ID to use when creating a child workflow. + /// + public string? InstanceId { get; init; } - internal new SubOrchestrationOptions ToDurableTaskOptions() + internal new SubOrchestrationOptions ToDurableTaskOptions() + { + TaskRetryOptions? retryOptions = null; + if (this.RetryPolicy is not null) { - TaskRetryOptions? retryOptions = null; - if (this.RetryPolicy is not null) - { - retryOptions = this.RetryPolicy.GetDurableRetryPolicy(); - } - - return new SubOrchestrationOptions(retryOptions, this.InstanceId); + retryOptions = this.RetryPolicy.GetDurableRetryPolicy(); } + + return new SubOrchestrationOptions(retryOptions, this.InstanceId); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/ActivationTests/DependencyInjectionActor.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/ActivationTests/DependencyInjectionActor.cs index 0182e023f..b40c3f5a0 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/ActivationTests/DependencyInjectionActor.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/ActivationTests/DependencyInjectionActor.cs @@ -14,31 +14,30 @@ using System.Threading.Tasks; using Dapr.Actors.Runtime; -namespace Dapr.Actors.AspNetCore.IntegrationTest.App.ActivationTests +namespace Dapr.Actors.AspNetCore.IntegrationTest.App.ActivationTests; + +public class DependencyInjectionActor : Actor, IDependencyInjectionActor { - public class DependencyInjectionActor : Actor, IDependencyInjectionActor - { - private readonly CounterService counter; + private readonly CounterService counter; - public DependencyInjectionActor(ActorHost host, CounterService counter) - : base(host) - { - this.counter = counter; - } - - public Task IncrementAsync() - { - return Task.FromResult(this.counter.Value++); - } - } - - public interface IDependencyInjectionActor : IActor + public DependencyInjectionActor(ActorHost host, CounterService counter) + : base(host) { - Task IncrementAsync(); + this.counter = counter; } - public class CounterService + public Task IncrementAsync() { - public int Value { get; set; } + return Task.FromResult(this.counter.Value++); } } + +public interface IDependencyInjectionActor : IActor +{ + Task IncrementAsync(); +} + +public class CounterService +{ + public int Value { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Program.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Program.cs index a6b47145a..5572b46b3 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Program.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Program.cs @@ -1,33 +1,32 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 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. -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +// Copyright 2021 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 Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -namespace Dapr.Actors.AspNetCore.IntegrationTest.App +namespace Dapr.Actors.AspNetCore.IntegrationTest.App; + +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + CreateHostBuilder(args).Build().Run(); } -} + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Startup.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Startup.cs index 6f010c158..5baae44bb 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Startup.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Startup.cs @@ -1,15 +1,15 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 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. -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +// Copyright 2021 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 Dapr.Actors.AspNetCore.IntegrationTest.App.ActivationTests; using Microsoft.AspNetCore.Builder; @@ -17,32 +17,31 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace Dapr.Actors.AspNetCore.IntegrationTest.App +namespace Dapr.Actors.AspNetCore.IntegrationTest.App; + +public class Startup { - public class Startup + public void ConfigureServices(IServiceCollection services) { - public void ConfigureServices(IServiceCollection services) + services.AddScoped(); + services.AddActors(options => { - services.AddScoped(); - services.AddActors(options => - { - options.Actors.RegisterActor(); - }); - } + options.Actors.RegisterActor(); + }); + } - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers(); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapActorsHandlers(); + }); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/ActivationTests.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest/ActivationTests.cs index 2fa61a6a9..83e66e9d8 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/ActivationTests.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/ActivationTests.cs @@ -19,68 +19,67 @@ using Xunit; using Xunit.Sdk; -namespace Dapr.Actors.AspNetCore.IntegrationTest +namespace Dapr.Actors.AspNetCore.IntegrationTest; + +public class ActivationTests { - public class ActivationTests + private readonly JsonSerializerOptions options = new JsonSerializerOptions() { - private readonly JsonSerializerOptions options = new JsonSerializerOptions() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true, - }; + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + }; - [Fact] - public async Task CanActivateActorWithDependencyInjection() - { - using var factory = new AppWebApplicationFactory(); - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + [Fact] + public async Task CanActivateActorWithDependencyInjection() + { + using var factory = new AppWebApplicationFactory(); + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - // Doing this twice verifies that the Actor stays active and retains state using DI. - var text = await IncrementCounterAsync(httpClient, "A"); - Assert.Equal("0", text); + // Doing this twice verifies that the Actor stays active and retains state using DI. + var text = await IncrementCounterAsync(httpClient, "A"); + Assert.Equal("0", text); - text = await IncrementCounterAsync(httpClient, "A"); - Assert.Equal("1", text); + text = await IncrementCounterAsync(httpClient, "A"); + Assert.Equal("1", text); - await DeactivateActor(httpClient, "A"); - } + await DeactivateActor(httpClient, "A"); + } - private async Task IncrementCounterAsync(HttpClient httpClient, string actorId) - { - var actorTypeName = nameof(DependencyInjectionActor); - var methodName = nameof(DependencyInjectionActor.IncrementAsync); + private async Task IncrementCounterAsync(HttpClient httpClient, string actorId) + { + var actorTypeName = nameof(DependencyInjectionActor); + var methodName = nameof(DependencyInjectionActor.IncrementAsync); - var request = new HttpRequestMessage(HttpMethod.Put, $"http://localhost/actors/{actorTypeName}/{actorId}/method/{methodName}"); - var response = await httpClient.SendAsync(request); - await Assert2XXStatusAsync(response); + var request = new HttpRequestMessage(HttpMethod.Put, $"http://localhost/actors/{actorTypeName}/{actorId}/method/{methodName}"); + var response = await httpClient.SendAsync(request); + await Assert2XXStatusAsync(response); - return await response.Content.ReadAsStringAsync(); - } + return await response.Content.ReadAsStringAsync(); + } - private async Task DeactivateActor(HttpClient httpClient, string actorId) - { - var actorTypeName = nameof(DependencyInjectionActor); + private async Task DeactivateActor(HttpClient httpClient, string actorId) + { + var actorTypeName = nameof(DependencyInjectionActor); - var request = new HttpRequestMessage(HttpMethod.Delete, $"http://localhost/actors/{actorTypeName}/{actorId}"); - var response = await httpClient.SendAsync(request); - await Assert2XXStatusAsync(response); - } + var request = new HttpRequestMessage(HttpMethod.Delete, $"http://localhost/actors/{actorTypeName}/{actorId}"); + var response = await httpClient.SendAsync(request); + await Assert2XXStatusAsync(response); + } - private async Task Assert2XXStatusAsync(HttpResponseMessage response) + private async Task Assert2XXStatusAsync(HttpResponseMessage response) + { + if (response.IsSuccessStatusCode) { - if (response.IsSuccessStatusCode) - { - return; - } - - if (response.Content == null) - { - throw new XunitException($"The response failed with a {response.StatusCode} and no body."); - } + return; + } - // We assume a textual response. #YOLO - var text = await response.Content.ReadAsStringAsync(); - throw new XunitException($"The response failed with a {response.StatusCode} and body:" + Environment.NewLine + text); + if (response.Content == null) + { + throw new XunitException($"The response failed with a {response.StatusCode} and no body."); } + + // We assume a textual response. #YOLO + var text = await response.Content.ReadAsStringAsync(); + throw new XunitException($"The response failed with a {response.StatusCode} and body:" + Environment.NewLine + text); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs index de27c9300..3362eb107 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs @@ -11,30 +11,29 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.AspNetCore.IntegrationTest -{ - using Dapr.Actors.AspNetCore.IntegrationTest.App; - using Microsoft.AspNetCore.Mvc.Testing; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; +namespace Dapr.Actors.AspNetCore.IntegrationTest; + +using Dapr.Actors.AspNetCore.IntegrationTest.App; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; - // The customizations here suppress logging from showing up in the console when - // running at the command line. - public class AppWebApplicationFactory : WebApplicationFactory +// The customizations here suppress logging from showing up in the console when +// running at the command line. +public class AppWebApplicationFactory : WebApplicationFactory +{ + protected override IHostBuilder CreateHostBuilder() { - protected override IHostBuilder CreateHostBuilder() + var builder = base.CreateHostBuilder(); + if (builder == null) { - var builder = base.CreateHostBuilder(); - if (builder == null) - { - return null; - } - - builder.ConfigureLogging(b => - { - b.SetMinimumLevel(LogLevel.None); - }); - return builder; + return null; } + + builder.ConfigureLogging(b => + { + b.SetMinimumLevel(LogLevel.None); + }); + return builder; } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs index bf3757ce1..0460b47c2 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs @@ -28,214 +28,213 @@ using Xunit; using Xunit.Sdk; -namespace Dapr.Actors.AspNetCore.IntegrationTest +namespace Dapr.Actors.AspNetCore.IntegrationTest; + +public class HostingTests { - public class HostingTests + [Fact] + public void MapActorsHandlers_WithoutAddActors_Throws() { - [Fact] - public void MapActorsHandlers_WithoutAddActors_Throws() + var exception = Assert.Throws(() => { - var exception = Assert.Throws(() => - { - // Initializes web pipeline which will trigger the exception we throw. - // - // NOTE: in 3.1 TestServer.CreateClient triggers the failure, in 5.0 it's Host.Start - using var host = CreateHost(); - var server = host.GetTestServer(); - server.CreateClient(); - }); + // Initializes web pipeline which will trigger the exception we throw. + // + // NOTE: in 3.1 TestServer.CreateClient triggers the failure, in 5.0 it's Host.Start + using var host = CreateHost(); + var server = host.GetTestServer(); + server.CreateClient(); + }); - Assert.Equal( - "The ActorRuntime service is not registered with the dependency injection container. " + - "Call AddActors() inside ConfigureServices() to register the actor runtime and actor types.", - exception.Message); - } + Assert.Equal( + "The ActorRuntime service is not registered with the dependency injection container. " + + "Call AddActors() inside ConfigureServices() to register the actor runtime and actor types.", + exception.Message); + } - [Fact] - public async Task MapActorsHandlers_IncludesHealthChecks() - { - using var factory = new AppWebApplicationFactory(); + [Fact] + public async Task MapActorsHandlers_IncludesHealthChecks() + { + using var factory = new AppWebApplicationFactory(); - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var response = await httpClient.GetAsync("/healthz"); - await Assert2XXStatusAsync(response); - } + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var response = await httpClient.GetAsync("/healthz"); + await Assert2XXStatusAsync(response); + } - [Fact] - public async Task ActorsHealthz_ShouldNotRequireAuthorization() - { - using var host = CreateHost(); - var server = host.GetTestServer(); + [Fact] + public async Task ActorsHealthz_ShouldNotRequireAuthorization() + { + using var host = CreateHost(); + var server = host.GetTestServer(); - var httpClient = server.CreateClient(); - var response = await httpClient.GetAsync("/healthz"); - await Assert2XXStatusAsync(response); - } + var httpClient = server.CreateClient(); + var response = await httpClient.GetAsync("/healthz"); + await Assert2XXStatusAsync(response); + } - // We add our own health check on /healthz with worse priority than one - // that would be added by a user. Make sure this works and the if the user - // adds their own health check it will win. - [Fact] - public async Task MapActorsHandlers_ActorHealthCheckDoesNotConflict() - { - using var host = CreateHost(); - var server = host.GetTestServer(); + // We add our own health check on /healthz with worse priority than one + // that would be added by a user. Make sure this works and the if the user + // adds their own health check it will win. + [Fact] + public async Task MapActorsHandlers_ActorHealthCheckDoesNotConflict() + { + using var host = CreateHost(); + var server = host.GetTestServer(); - var httpClient = server.CreateClient(); - var response = await httpClient.GetAsync("/healthz"); - await Assert2XXStatusAsync(response); + var httpClient = server.CreateClient(); + var response = await httpClient.GetAsync("/healthz"); + await Assert2XXStatusAsync(response); - var text = await response.Content.ReadAsStringAsync(); - Assert.Equal("Ice Cold, Solid Gold!", text); - } + var text = await response.Content.ReadAsStringAsync(); + Assert.Equal("Ice Cold, Solid Gold!", text); + } - // Regression test for #434 - [Fact] - public async Task MapActorsHandlers_WorksWithFallbackRoute() - { - using var host = CreateHost(); - var server = host.GetTestServer(); + // Regression test for #434 + [Fact] + public async Task MapActorsHandlers_WorksWithFallbackRoute() + { + using var host = CreateHost(); + var server = host.GetTestServer(); - var httpClient = server.CreateClient(); - var response = await httpClient.GetAsync("/dapr/config"); - await Assert2XXStatusAsync(response); - } + var httpClient = server.CreateClient(); + var response = await httpClient.GetAsync("/dapr/config"); + await Assert2XXStatusAsync(response); + } - private static IHost CreateHost() where TStartup : class - { - var builder = Host - .CreateDefaultBuilder() - .ConfigureLogging(b => - { - // shhhh - b.SetMinimumLevel(LogLevel.None); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - webBuilder.UseTestServer(); - }); - var host = builder.Build(); - try + private static IHost CreateHost() where TStartup : class + { + var builder = Host + .CreateDefaultBuilder() + .ConfigureLogging(b => { - host.Start(); - } - catch + // shhhh + b.SetMinimumLevel(LogLevel.None); + }) + .ConfigureWebHostDefaults(webBuilder => { - host.Dispose(); - throw; - } - - return host; + webBuilder.UseStartup(); + webBuilder.UseTestServer(); + }); + var host = builder.Build(); + try + { + host.Start(); } + catch + { + host.Dispose(); + throw; + } + + return host; + } - private class BadStartup + private class BadStartup + { + public void ConfigureServices(IServiceCollection services) { - public void ConfigureServices(IServiceCollection services) - { - // no call to AddActors here. That's bad! - services.AddRouting(); - services.AddHealthChecks(); - } + // no call to AddActors here. That's bad! + services.AddRouting(); + services.AddHealthChecks(); + } - public void Configure(IApplicationBuilder app) + public void Configure(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(endpoints => { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers(); - }); - } + endpoints.MapActorsHandlers(); + }); } + } - private class AuthorizedRoutesStartup + private class AuthorizedRoutesStartup + { + public void ConfigureServices(IServiceCollection services) { - public void ConfigureServices(IServiceCollection services) - { - services.AddActors(default); - services.AddAuthentication().AddDapr(options => options.Token = "abcdefg"); + services.AddActors(default); + services.AddAuthentication().AddDapr(options => options.Token = "abcdefg"); - services.AddAuthorization(o => o.AddDapr()); - } + services.AddAuthorization(o => o.AddDapr()); + } - public void Configure(IApplicationBuilder app) + public void Configure(IApplicationBuilder app) + { + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => { - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers().RequireAuthorization("Dapr"); - }); - } + endpoints.MapActorsHandlers().RequireAuthorization("Dapr"); + }); } + } - private class FallbackRouteStartup + private class FallbackRouteStartup + { + public void ConfigureServices(IServiceCollection services) { - public void ConfigureServices(IServiceCollection services) - { - services.AddActors(default); - } + services.AddActors(default); + } - public void Configure(IApplicationBuilder app) + public void Configure(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(endpoints => { - app.UseRouting(); - app.UseEndpoints(endpoints => + // This routing feature registers a "route of last resort" which is what + // was tripping out Actors prior to changing how they are registered. + endpoints.MapFallback(context => { - // This routing feature registers a "route of last resort" which is what - // was tripping out Actors prior to changing how they are registered. - endpoints.MapFallback(context => - { - throw new InvalidTimeZoneException("This should not be called!"); - }); - endpoints.MapActorsHandlers(); + throw new InvalidTimeZoneException("This should not be called!"); }); - } + endpoints.MapActorsHandlers(); + }); } + } - private class HealthCheckStartup + private class HealthCheckStartup + { + public void ConfigureServices(IServiceCollection services) { - public void ConfigureServices(IServiceCollection services) - { - services.AddActors(default); - } + services.AddActors(default); + } - public void Configure(IApplicationBuilder app) + public void Configure(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(endpoints => { - app.UseRouting(); - app.UseEndpoints(endpoints => + endpoints.MapHealthChecks("/healthz", new HealthCheckOptions() { - endpoints.MapHealthChecks("/healthz", new HealthCheckOptions() + // Write something different so we know this one is called. + ResponseWriter = async (httpContext, report) => { - // Write something different so we know this one is called. - ResponseWriter = async (httpContext, report) => - { - await httpContext.Response.WriteAsync( - report.Status == HealthStatus.Healthy ? + await httpContext.Response.WriteAsync( + report.Status == HealthStatus.Healthy ? "Ice Cold, Solid Gold!" : "Oh Noes!"); - }, - }); - endpoints.MapActorsHandlers(); + }, }); - } + endpoints.MapActorsHandlers(); + }); } + } - private async Task Assert2XXStatusAsync(HttpResponseMessage response) + private async Task Assert2XXStatusAsync(HttpResponseMessage response) + { + if (response.IsSuccessStatusCode) { - if (response.IsSuccessStatusCode) - { - return; - } - - if (response.Content == null) - { - throw new XunitException($"The response failed with a {response.StatusCode} and no body."); - } + return; + } - // We assume a textual response. #YOLO - var text = await response.Content.ReadAsStringAsync(); - throw new XunitException($"The response failed with a {response.StatusCode} and body:" + Environment.NewLine + text); + if (response.Content == null) + { + throw new XunitException($"The response failed with a {response.StatusCode} and no body."); } + + // We assume a textual response. #YOLO + var text = await response.Content.ReadAsStringAsync(); + throw new XunitException($"The response failed with a {response.StatusCode} and body:" + Environment.NewLine + text); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs b/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs index 8e34eaffd..53c8ec9e0 100644 --- a/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs +++ b/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs @@ -18,94 +18,93 @@ using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Dapr.Actors.AspNetCore +namespace Dapr.Actors.AspNetCore; + +public class ActorHostingTest { - public class ActorHostingTest + [Fact] + public void CanRegisterActorsInSingleCalls() { - [Fact] - public void CanRegisterActorsInSingleCalls() + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddActors(options => { - var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddActors(options => - { - options.Actors.RegisterActor(); - options.Actors.RegisterActor(); - }); + options.Actors.RegisterActor(); + options.Actors.RegisterActor(); + }); - var runtime = services.BuildServiceProvider().GetRequiredService(); + var runtime = services.BuildServiceProvider().GetRequiredService(); - Assert.Collection( - runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t), - t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t), - t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t)); - } + Assert.Collection( + runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t), + t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t), + t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t)); + } - [Fact] - public void CanRegisterActorsInMultipleCalls() + [Fact] + public void CanRegisterActorsInMultipleCalls() + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddActors(options => { - var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddActors(options => - { - options.Actors.RegisterActor(); - }); + options.Actors.RegisterActor(); + }); - services.AddActors(options => - { - options.Actors.RegisterActor(); - }); + services.AddActors(options => + { + options.Actors.RegisterActor(); + }); - var runtime = services.BuildServiceProvider().GetRequiredService(); + var runtime = services.BuildServiceProvider().GetRequiredService(); - Assert.Collection( - runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t), - t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t), - t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t)); - } + Assert.Collection( + runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t), + t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t), + t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t)); + } - [Fact] - public void CanAccessProxyFactoryWithCustomJsonOptions() - { - var jsonOptions = new JsonSerializerOptions(); + [Fact] + public void CanAccessProxyFactoryWithCustomJsonOptions() + { + var jsonOptions = new JsonSerializerOptions(); - var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddActors(options => - { - options.JsonSerializerOptions = jsonOptions; - }); + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddActors(options => + { + options.JsonSerializerOptions = jsonOptions; + }); - services.AddActors(options => - { - options.Actors.RegisterActor(); - }); + services.AddActors(options => + { + options.Actors.RegisterActor(); + }); - var factory = (ActorProxyFactory)services.BuildServiceProvider().GetRequiredService(); - Assert.Same(jsonOptions, factory.DefaultOptions.JsonSerializerOptions); - } + var factory = (ActorProxyFactory)services.BuildServiceProvider().GetRequiredService(); + Assert.Same(jsonOptions, factory.DefaultOptions.JsonSerializerOptions); + } - private interface ITestActor : IActor - { - } + private interface ITestActor : IActor + { + } - private class TestActor1 : Actor, ITestActor + private class TestActor1 : Actor, ITestActor + { + public TestActor1(ActorHost host) + : base(host) { - public TestActor1(ActorHost host) - : base(host) - { - } } + } - private class TestActor2 : Actor, ITestActor + private class TestActor2 : Actor, ITestActor + { + public TestActor2(ActorHost host) + : base(host) { - public TestActor2(ActorHost host) - : base(host) - { - } } } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.Test/Runtime/ActorsEndpointRouteBuilderExtensionsTests.cs b/test/Dapr.Actors.AspNetCore.Test/Runtime/ActorsEndpointRouteBuilderExtensionsTests.cs index e02ba39b6..9fc67760e 100644 --- a/test/Dapr.Actors.AspNetCore.Test/Runtime/ActorsEndpointRouteBuilderExtensionsTests.cs +++ b/test/Dapr.Actors.AspNetCore.Test/Runtime/ActorsEndpointRouteBuilderExtensionsTests.cs @@ -21,82 +21,81 @@ using Microsoft.Extensions.Logging; using Xunit; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +public class ActorsEndpointRouteBuilderExtensionsTests { - public class ActorsEndpointRouteBuilderExtensionsTests + [Fact] + public async Task MapActorsHandlers_MapDaprConfigEndpoint() { - [Fact] - public async Task MapActorsHandlers_MapDaprConfigEndpoint() + using var host = CreateHost(options => { - using var host = CreateHost(options => - { - options.Actors.RegisterActor(); - }); - var server = host.GetTestServer(); + options.Actors.RegisterActor(); + }); + var server = host.GetTestServer(); - var httpClient = server.CreateClient(); - var response = await httpClient.GetAsync("/dapr/config"); + var httpClient = server.CreateClient(); + var response = await httpClient.GetAsync("/dapr/config"); - var text = await response.Content.ReadAsStringAsync(); - Assert.Equal(@"{""entities"":[""TestActor""],""reentrancy"":{""enabled"":false}}", text); - } + var text = await response.Content.ReadAsStringAsync(); + Assert.Equal(@"{""entities"":[""TestActor""],""reentrancy"":{""enabled"":false}}", text); + } - private static IHost CreateHost(Action configure) where TStartup : class - { - var builder = Host - .CreateDefaultBuilder() - .ConfigureLogging(b => - { - // shhhh - b.SetMinimumLevel(LogLevel.None); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - webBuilder.UseTestServer(); - }) - .ConfigureServices(services => - { - services.AddActors(configure); - }); - var host = builder.Build(); - try + private static IHost CreateHost(Action configure) where TStartup : class + { + var builder = Host + .CreateDefaultBuilder() + .ConfigureLogging(b => { - host.Start(); - } - catch + // shhhh + b.SetMinimumLevel(LogLevel.None); + }) + .ConfigureWebHostDefaults(webBuilder => { - host.Dispose(); - throw; - } - - return host; + webBuilder.UseStartup(); + webBuilder.UseTestServer(); + }) + .ConfigureServices(services => + { + services.AddActors(configure); + }); + var host = builder.Build(); + try + { + host.Start(); } - - private class ActorsStartup + catch { - public void ConfigureServices(IServiceCollection services) - { - services.AddActors(default); - } - - public void Configure(IApplicationBuilder app) - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers(); - }); - } + host.Dispose(); + throw; } - private interface ITestActor : IActor + return host; + } + + private class ActorsStartup + { + public void ConfigureServices(IServiceCollection services) { + services.AddActors(default); } - private class TestActor : Actor, ITestActor + public void Configure(IApplicationBuilder app) { - public TestActor(ActorHost host) : base(host) { } + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapActorsHandlers(); + }); } } -} + + private interface ITestActor : IActor + { + } + + private class TestActor : Actor, ITestActor + { + public TestActor(ActorHost host) : base(host) { } + } +} \ No newline at end of file diff --git a/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs b/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs index 9c3b55365..0ebb8d592 100644 --- a/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs +++ b/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs @@ -16,169 +16,168 @@ using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +public class DependencyInjectionActorActivatorTests { - public class DependencyInjectionActorActivatorTests + private IServiceProvider CreateServices() { - private IServiceProvider CreateServices() - { - var services = new ServiceCollection(); - services.AddScoped(); - services.AddSingleton(); - return services.BuildServiceProvider(new ServiceProviderOptions() { ValidateScopes = true, }); - } + var services = new ServiceCollection(); + services.AddScoped(); + services.AddSingleton(); + return services.BuildServiceProvider(new ServiceProviderOptions() { ValidateScopes = true, }); + } - private DependencyInjectionActorActivator CreateActivator(Type type) - { - return new DependencyInjectionActorActivator(CreateServices(), ActorTypeInformation.Get(type, actorTypeName: null)); - } + private DependencyInjectionActorActivator CreateActivator(Type type) + { + return new DependencyInjectionActorActivator(CreateServices(), ActorTypeInformation.Get(type, actorTypeName: null)); + } - [Fact] - public async Task CreateAsync_CanActivateWithDI() - { - var activator = CreateActivator(typeof(TestActor)); + [Fact] + public async Task CreateAsync_CanActivateWithDI() + { + var activator = CreateActivator(typeof(TestActor)); - var host = ActorHost.CreateForTest(); - var state = await activator.CreateAsync(host); - var actor = Assert.IsType(state.Actor); + var host = ActorHost.CreateForTest(); + var state = await activator.CreateAsync(host); + var actor = Assert.IsType(state.Actor); - Assert.NotNull(actor.SingletonService); - Assert.NotNull(actor.ScopedService); - } + Assert.NotNull(actor.SingletonService); + Assert.NotNull(actor.ScopedService); + } - [Fact] - public async Task CreateAsync_CreatesNewScope() - { - var activator = CreateActivator(typeof(TestActor)); + [Fact] + public async Task CreateAsync_CreatesNewScope() + { + var activator = CreateActivator(typeof(TestActor)); - var host1 = ActorHost.CreateForTest(); - var state1 = await activator.CreateAsync(host1); - var actor1 = Assert.IsType(state1.Actor); + var host1 = ActorHost.CreateForTest(); + var state1 = await activator.CreateAsync(host1); + var actor1 = Assert.IsType(state1.Actor); - var host2 = ActorHost.CreateForTest(); - var state2 = await activator.CreateAsync(host2); - var actor2 = Assert.IsType(state2.Actor); + var host2 = ActorHost.CreateForTest(); + var state2 = await activator.CreateAsync(host2); + var actor2 = Assert.IsType(state2.Actor); - Assert.Same(actor1.SingletonService, actor2.SingletonService); - Assert.NotSame(actor1.ScopedService, actor2.ScopedService); - } + Assert.Same(actor1.SingletonService, actor2.SingletonService); + Assert.NotSame(actor1.ScopedService, actor2.ScopedService); + } - [Fact] - public async Task CreateAsync_CustomJsonOptions() - { - var jsonOptions = new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = false }; - var activator = CreateActivator(typeof(TestActor)); + [Fact] + public async Task CreateAsync_CustomJsonOptions() + { + var jsonOptions = new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = false }; + var activator = CreateActivator(typeof(TestActor)); - var host = ActorHost.CreateForTest(new ActorTestOptions { JsonSerializerOptions = jsonOptions }); - var state = await activator.CreateAsync(host); + var host = ActorHost.CreateForTest(new ActorTestOptions { JsonSerializerOptions = jsonOptions }); + var state = await activator.CreateAsync(host); - Assert.Same(jsonOptions, state.Actor.Host.JsonSerializerOptions); - } + Assert.Same(jsonOptions, state.Actor.Host.JsonSerializerOptions); + } - [Fact] - public async Task DeleteAsync_DisposesScope() - { - var activator = CreateActivator(typeof(TestActor)); + [Fact] + public async Task DeleteAsync_DisposesScope() + { + var activator = CreateActivator(typeof(TestActor)); - var host = ActorHost.CreateForTest(); - var state = await activator.CreateAsync(host); - var actor = Assert.IsType(state.Actor); + var host = ActorHost.CreateForTest(); + var state = await activator.CreateAsync(host); + var actor = Assert.IsType(state.Actor); - Assert.False(actor.ScopedService.IsDisposed); + Assert.False(actor.ScopedService.IsDisposed); - await activator.DeleteAsync(state); + await activator.DeleteAsync(state); - Assert.True(actor.ScopedService.IsDisposed); - } + Assert.True(actor.ScopedService.IsDisposed); + } - [Fact] - public async Task DeleteAsync_Disposable() - { - var activator = CreateActivator(typeof(DisposableActor)); + [Fact] + public async Task DeleteAsync_Disposable() + { + var activator = CreateActivator(typeof(DisposableActor)); - var host = ActorHost.CreateForTest(); - var state = await activator.CreateAsync(host); - var actor = Assert.IsType(state.Actor); + var host = ActorHost.CreateForTest(); + var state = await activator.CreateAsync(host); + var actor = Assert.IsType(state.Actor); - await activator.DeleteAsync(state); // does not throw + await activator.DeleteAsync(state); // does not throw - Assert.True(actor.IsDisposed); - } + Assert.True(actor.IsDisposed); + } - [Fact] - public async Task DeleteAsync_AsyncDisposable() - { - var activator = CreateActivator(typeof(AsyncDisposableActor)); + [Fact] + public async Task DeleteAsync_AsyncDisposable() + { + var activator = CreateActivator(typeof(AsyncDisposableActor)); - var host = ActorHost.CreateForTest(); - var state = await activator.CreateAsync(host); - var actor = Assert.IsType(state.Actor); + var host = ActorHost.CreateForTest(); + var state = await activator.CreateAsync(host); + var actor = Assert.IsType(state.Actor); - await activator.DeleteAsync(state); + await activator.DeleteAsync(state); - Assert.True(actor.IsDisposed); - } + Assert.True(actor.IsDisposed); + } - private class TestSingletonService + private class TestSingletonService + { + } + + private class TestScopedService : IDisposable + { + public bool IsDisposed { get; set; } + + public void Dispose() { + IsDisposed = true; } + } - private class TestScopedService : IDisposable - { - public bool IsDisposed { get; set; } + private interface ITestActor : IActor + { + } - public void Dispose() - { - IsDisposed = true; - } + private class TestActor : Actor, ITestActor + { + public TestActor(ActorHost host, TestSingletonService singletonService, TestScopedService scopedService) + : base(host) + { + this.SingletonService = singletonService; + this.ScopedService = scopedService; } - private interface ITestActor : IActor + public TestSingletonService SingletonService { get; } + public TestScopedService ScopedService { get; } + } + + private class DisposableActor : Actor, ITestActor, IDisposable + { + public DisposableActor(ActorHost host) + : base(host) { } - private class TestActor : Actor, ITestActor + public bool IsDisposed { get; set; } + + public void Dispose() { - public TestActor(ActorHost host, TestSingletonService singletonService, TestScopedService scopedService) - : base(host) - { - this.SingletonService = singletonService; - this.ScopedService = scopedService; - } - - public TestSingletonService SingletonService { get; } - public TestScopedService ScopedService { get; } + IsDisposed = true; } + } - private class DisposableActor : Actor, ITestActor, IDisposable + private class AsyncDisposableActor : Actor, ITestActor, IAsyncDisposable + { + public AsyncDisposableActor(ActorHost host) + : base(host) { - public DisposableActor(ActorHost host) - : base(host) - { - } - - public bool IsDisposed { get; set; } - - public void Dispose() - { - IsDisposed = true; - } } - private class AsyncDisposableActor : Actor, ITestActor, IAsyncDisposable + public bool IsDisposed { get; set; } + + public ValueTask DisposeAsync() { - public AsyncDisposableActor(ActorHost host) - : base(host) - { - } - - public bool IsDisposed { get; set; } - - public ValueTask DisposeAsync() - { - IsDisposed = true; - return new ValueTask(); - } + IsDisposed = true; + return new ValueTask(); } } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs index 97dbcfe1e..5b73fb95a 100644 --- a/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs +++ b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs @@ -1,52 +1,51 @@ using Dapr.Actors.Generators.Extensions; -namespace Dapr.Actors.Generators.Test.Extensions +namespace Dapr.Actors.Generators.Test.Extensions; + +public class IEnumerableExtensionsTests { - public class IEnumerableExtensionsTests + [Fact] + public void IndexOf_WhenPredicateIsNull_ThrowsArgumentNullException() { - [Fact] - public void IndexOf_WhenPredicateIsNull_ThrowsArgumentNullException() - { - // Arrange - var source = new[] { 1, 2, 3, 4, 5 }; - Func predicate = null!; - - // Act - Action act = () => source.IndexOf(predicate); - - // Assert - Assert.Throws(act); - } - - [Theory] - [InlineData(new int[] { }, 3, -1)] - [InlineData(new[] { 1, 2, 3, 4, 5 }, 6, -1)] - public void IndexOf_WhenItemDoesNotExist_ReturnsMinusOne(int[] source, int item, int expected) - { - // Arrange - Func predicate = (x) => x == item; - - // Act - var index = source.IndexOf(predicate); - - // Assert - Assert.Equal(expected, index); - } - - [Theory] - [InlineData(new[] { 1, 2, 3, 4, 5 }, 3, 2)] - [InlineData(new[] { 1, 2, 3, 4, 5 }, 1, 0)] - [InlineData(new[] { 1, 2, 3, 4, 5 }, 5, 4)] - public void IndexOf_WhenItemExists_ReturnsIndexOfItem(int[] source, int item, int expected) - { - // Arrange - Func predicate = (x) => x == item; - - // Act - var index = source.IndexOf(predicate); - - // Assert - Assert.Equal(expected, index); - } + // Arrange + var source = new[] { 1, 2, 3, 4, 5 }; + Func predicate = null!; + + // Act + Action act = () => source.IndexOf(predicate); + + // Assert + Assert.Throws(act); + } + + [Theory] + [InlineData(new int[] { }, 3, -1)] + [InlineData(new[] { 1, 2, 3, 4, 5 }, 6, -1)] + public void IndexOf_WhenItemDoesNotExist_ReturnsMinusOne(int[] source, int item, int expected) + { + // Arrange + Func predicate = (x) => x == item; + + // Act + var index = source.IndexOf(predicate); + + // Assert + Assert.Equal(expected, index); + } + + [Theory] + [InlineData(new[] { 1, 2, 3, 4, 5 }, 3, 2)] + [InlineData(new[] { 1, 2, 3, 4, 5 }, 1, 0)] + [InlineData(new[] { 1, 2, 3, 4, 5 }, 5, 4)] + public void IndexOf_WhenItemExists_ReturnsIndexOfItem(int[] source, int item, int expected) + { + // Arrange + Func predicate = (x) => x == item; + + // Act + var index = source.IndexOf(predicate); + + // Assert + Assert.Equal(expected, index); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index 807bd7469..6d36cc122 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -2,132 +2,131 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -namespace Dapr.Actors.Generators.Test.Helpers +namespace Dapr.Actors.Generators.Test.Helpers; + +public class SyntaxFactoryHelpersTests { - public class SyntaxFactoryHelpersTests + [Fact] + public void ThrowArgumentNullException_GenerateThrowArgumentNullExceptionSyntaxWithGivenArgumentName() { - [Fact] - public void ThrowArgumentNullException_GenerateThrowArgumentNullExceptionSyntaxWithGivenArgumentName() - { - // Arrange - var argumentName = "arg0"; - var expectedSource = $@"throw new System.ArgumentNullException(nameof(arg0));"; - var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); - - // Act - var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullException(argumentName)) - .SyntaxTree - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); - - // Assert - Assert.Equal(expectedSourceNormalized, generatedSource); - } + // Arrange + var argumentName = "arg0"; + var expectedSource = $@"throw new System.ArgumentNullException(nameof(arg0));"; + var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Act + var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullException(argumentName)) + .SyntaxTree + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Assert + Assert.Equal(expectedSourceNormalized, generatedSource); + } - [Fact] - public void ThrowIfArgumentNullException_GivesNullCheckSyntaxWithGivenArgumentName() - { - // Arrange - var argumentName = "arg0"; - var expectedSource = $@"if (arg0 is null) + [Fact] + public void ThrowIfArgumentNullException_GivesNullCheckSyntaxWithGivenArgumentName() + { + // Arrange + var argumentName = "arg0"; + var expectedSource = $@"if (arg0 is null) {{ throw new System.ArgumentNullException(nameof(arg0)); }}"; - var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); - - // Act - var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNull(argumentName) - .SyntaxTree - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); - - // Assert - Assert.Equal(expectedSourceNormalized, generatedSource); - } + var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Act + var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNull(argumentName) + .SyntaxTree + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Assert + Assert.Equal(expectedSourceNormalized, generatedSource); + } - [Fact] - public void ActorProxyInvokeMethodAsync_WithoutReturnTypeAndParamters_ReturnNonGenericInvokeMethodAsync() - { - // Arrange - var remoteMethodName = "RemoteMethodToCall"; - var remoteMethodParameters = Array.Empty(); - var remoteMethodReturnTypes = Array.Empty(); - var actorProxMemberAccessSyntax = SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.ThisExpression(), - SyntaxFactory.IdentifierName("actorProxy") - ); - var expectedSource = $@"this.actorProxy.InvokeMethodAsync(""RemoteMethodToCall"")"; - var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); - - // Act - var generatedSource = SyntaxFactoryHelpers.ActorProxyInvokeMethodAsync( + [Fact] + public void ActorProxyInvokeMethodAsync_WithoutReturnTypeAndParamters_ReturnNonGenericInvokeMethodAsync() + { + // Arrange + var remoteMethodName = "RemoteMethodToCall"; + var remoteMethodParameters = Array.Empty(); + var remoteMethodReturnTypes = Array.Empty(); + var actorProxMemberAccessSyntax = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + SyntaxFactory.IdentifierName("actorProxy") + ); + var expectedSource = $@"this.actorProxy.InvokeMethodAsync(""RemoteMethodToCall"")"; + var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Act + var generatedSource = SyntaxFactoryHelpers.ActorProxyInvokeMethodAsync( actorProxMemberAccessSyntax, remoteMethodName, remoteMethodParameters, remoteMethodReturnTypes) - .SyntaxTree - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); ; + .SyntaxTree + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); ; - // Assert - Assert.Equal(expectedSourceNormalized, generatedSource); - } - - [Fact] - public void NameOfExpression() - { - // Arrange - var argumentName = "arg0"; - var expectedSource = $@"nameof(arg0)"; - var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); - - // Act - var generatedSource = SyntaxFactoryHelpers.NameOfExpression(argumentName) - .SyntaxTree - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); - - // Assert - Assert.Equal(expectedSourceNormalized, generatedSource); - } + // Assert + Assert.Equal(expectedSourceNormalized, generatedSource); + } - [Theory] - [InlineData(Accessibility.Public, new[] { SyntaxKind.PublicKeyword })] - [InlineData(Accessibility.Internal, new[] { SyntaxKind.InternalKeyword })] - [InlineData(Accessibility.Private, new[] { SyntaxKind.PrivateKeyword })] - [InlineData(Accessibility.Protected, new[] { SyntaxKind.ProtectedKeyword })] - [InlineData(Accessibility.ProtectedAndInternal, new[] { SyntaxKind.ProtectedKeyword, SyntaxKind.InternalKeyword })] - public void GetSyntaxKinds_GenerateSyntaxForGivenAccessibility(Accessibility accessibility, ICollection expectedSyntaxKinds) - { - // Arrange + [Fact] + public void NameOfExpression() + { + // Arrange + var argumentName = "arg0"; + var expectedSource = $@"nameof(arg0)"; + var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Act + var generatedSource = SyntaxFactoryHelpers.NameOfExpression(argumentName) + .SyntaxTree + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Assert + Assert.Equal(expectedSourceNormalized, generatedSource); + } - // Act - var generatedSyntaxKinds = SyntaxFactoryHelpers.GetSyntaxKinds(accessibility); + [Theory] + [InlineData(Accessibility.Public, new[] { SyntaxKind.PublicKeyword })] + [InlineData(Accessibility.Internal, new[] { SyntaxKind.InternalKeyword })] + [InlineData(Accessibility.Private, new[] { SyntaxKind.PrivateKeyword })] + [InlineData(Accessibility.Protected, new[] { SyntaxKind.ProtectedKeyword })] + [InlineData(Accessibility.ProtectedAndInternal, new[] { SyntaxKind.ProtectedKeyword, SyntaxKind.InternalKeyword })] + public void GetSyntaxKinds_GenerateSyntaxForGivenAccessibility(Accessibility accessibility, ICollection expectedSyntaxKinds) + { + // Arrange - // Assert - foreach (var expectedSyntaxKind in expectedSyntaxKinds) - { - Assert.Contains(expectedSyntaxKind, generatedSyntaxKinds); - } + // Act + var generatedSyntaxKinds = SyntaxFactoryHelpers.GetSyntaxKinds(accessibility); - Assert.Equal(expectedSyntaxKinds.Count, generatedSyntaxKinds.Count); + // Assert + foreach (var expectedSyntaxKind in expectedSyntaxKinds) + { + Assert.Contains(expectedSyntaxKind, generatedSyntaxKinds); } + + Assert.Equal(expectedSyntaxKinds.Count, generatedSyntaxKinds.Count); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/ActorStateManagerTest.cs b/test/Dapr.Actors.Test/ActorStateManagerTest.cs index a4e0e4140..ae15e89cd 100644 --- a/test/Dapr.Actors.Test/ActorStateManagerTest.cs +++ b/test/Dapr.Actors.Test/ActorStateManagerTest.cs @@ -11,182 +11,181 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using Xunit; +using Dapr.Actors.Communication; +using Dapr.Actors.Runtime; +using Moq; + +/// +/// Contains tests for ActorStateManager. +/// +public class ActorStateManagerTest { - using System; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using System.Collections.Generic; - using Xunit; - using Dapr.Actors.Communication; - using Dapr.Actors.Runtime; - using Moq; - - /// - /// Contains tests for ActorStateManager. - /// - public class ActorStateManagerTest + [Fact] + public async Task SetGet() { - [Fact] - public async Task SetGet() - { - var interactor = new Mock(); - var host = ActorHost.CreateForTest(); - host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var mngr = new ActorStateManager(new TestActor(host)); - var token = new CancellationToken(); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("", null))); - - await mngr.AddStateAsync("key1", "value1", token); - await mngr.AddStateAsync("key2", "value2", token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - - await Assert.ThrowsAsync(() => mngr.AddStateAsync("key1", "value3", token)); - await Assert.ThrowsAsync(() => mngr.AddStateAsync("key2", "value4", token)); - - await mngr.SetStateAsync("key1", "value5", token); - await mngr.SetStateAsync("key2", "value6", token); - Assert.Equal("value5", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value6", await mngr.GetStateAsync("key2", token)); - } - - [Fact] - public async Task StateWithTTL() - { - var interactor = new Mock(); - var host = ActorHost.CreateForTest(); - host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var mngr = new ActorStateManager(new TestActor(host)); - var token = new CancellationToken(); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("", null))); - - await mngr.AddStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); - await mngr.AddStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - - await Task.Delay(TimeSpan.FromSeconds(1.5)); - - await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); - await Assert.ThrowsAsync(() => mngr.GetStateAsync("key2", token)); - - // Should be able to add state again after expiry and should not expire. - await mngr.AddStateAsync("key1", "value1", token); - await mngr.AddStateAsync("key2", "value2", token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - await Task.Delay(TimeSpan.FromSeconds(1.5)); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - } - - [Fact] - public async Task StateRemoveAddTTL() - { - var interactor = new Mock(); - var host = ActorHost.CreateForTest(); - host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var mngr = new ActorStateManager(new TestActor(host)); - var token = new CancellationToken(); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("", null))); - - await mngr.AddStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); - await mngr.AddStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - - await mngr.SetStateAsync("key1", "value1", token); - await mngr.SetStateAsync("key2", "value2", token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - - // TTL is removed so state should not expire. - await Task.Delay(TimeSpan.FromSeconds(1.5)); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - - // Adding TTL back should expire state. - await mngr.SetStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); - await mngr.SetStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - await Task.Delay(TimeSpan.FromSeconds(1.5)); - await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); - await Assert.ThrowsAsync(() => mngr.GetStateAsync("key2", token)); - } - - [Fact] - public async Task StateDaprdExpireTime() - { - var interactor = new Mock(); - var host = ActorHost.CreateForTest(); - host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var mngr = new ActorStateManager(new TestActor(host)); - var token = new CancellationToken(); - - // Existing key which has an expiry time. - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("\"value1\"", DateTime.UtcNow.AddSeconds(1)))); - - await Assert.ThrowsAsync(() => mngr.AddStateAsync("key1", "value3", token)); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - - // No longer return the value from the state provider. - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("", null))); - - // Key should be expired after 1 seconds. - await Task.Delay(TimeSpan.FromSeconds(1.5)); - await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); - await Assert.ThrowsAsync(() => mngr.RemoveStateAsync("key1", token)); - await mngr.AddStateAsync("key1", "value2", TimeSpan.FromSeconds(1), token); - Assert.Equal("value2", await mngr.GetStateAsync("key1", token)); - } - - [Fact] - public async Task RemoveState() - { - var interactor = new Mock(); - var host = ActorHost.CreateForTest(); - host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var mngr = new ActorStateManager(new TestActor(host)); - var token = new CancellationToken(); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("", null))); - - await Assert.ThrowsAsync(() => mngr.RemoveStateAsync("key1", token)); - - await mngr.AddStateAsync("key1", "value1", token); - await mngr.AddStateAsync("key2", "value2", token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - - await mngr.RemoveStateAsync("key1", token); - await mngr.RemoveStateAsync("key2", token); - - await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); - await Assert.ThrowsAsync(() => mngr.GetStateAsync("key2", token)); - - // Should be able to add state again after removal. - await mngr.AddStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); - await mngr.AddStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); - Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); - Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); - } + var interactor = new Mock(); + var host = ActorHost.CreateForTest(); + host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var mngr = new ActorStateManager(new TestActor(host)); + var token = new CancellationToken(); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("", null))); + + await mngr.AddStateAsync("key1", "value1", token); + await mngr.AddStateAsync("key2", "value2", token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + + await Assert.ThrowsAsync(() => mngr.AddStateAsync("key1", "value3", token)); + await Assert.ThrowsAsync(() => mngr.AddStateAsync("key2", "value4", token)); + + await mngr.SetStateAsync("key1", "value5", token); + await mngr.SetStateAsync("key2", "value6", token); + Assert.Equal("value5", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value6", await mngr.GetStateAsync("key2", token)); } -} + + [Fact] + public async Task StateWithTTL() + { + var interactor = new Mock(); + var host = ActorHost.CreateForTest(); + host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var mngr = new ActorStateManager(new TestActor(host)); + var token = new CancellationToken(); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("", null))); + + await mngr.AddStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); + await mngr.AddStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + + await Task.Delay(TimeSpan.FromSeconds(1.5)); + + await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); + await Assert.ThrowsAsync(() => mngr.GetStateAsync("key2", token)); + + // Should be able to add state again after expiry and should not expire. + await mngr.AddStateAsync("key1", "value1", token); + await mngr.AddStateAsync("key2", "value2", token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + await Task.Delay(TimeSpan.FromSeconds(1.5)); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + } + + [Fact] + public async Task StateRemoveAddTTL() + { + var interactor = new Mock(); + var host = ActorHost.CreateForTest(); + host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var mngr = new ActorStateManager(new TestActor(host)); + var token = new CancellationToken(); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("", null))); + + await mngr.AddStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); + await mngr.AddStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + + await mngr.SetStateAsync("key1", "value1", token); + await mngr.SetStateAsync("key2", "value2", token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + + // TTL is removed so state should not expire. + await Task.Delay(TimeSpan.FromSeconds(1.5)); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + + // Adding TTL back should expire state. + await mngr.SetStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); + await mngr.SetStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + await Task.Delay(TimeSpan.FromSeconds(1.5)); + await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); + await Assert.ThrowsAsync(() => mngr.GetStateAsync("key2", token)); + } + + [Fact] + public async Task StateDaprdExpireTime() + { + var interactor = new Mock(); + var host = ActorHost.CreateForTest(); + host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var mngr = new ActorStateManager(new TestActor(host)); + var token = new CancellationToken(); + + // Existing key which has an expiry time. + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("\"value1\"", DateTime.UtcNow.AddSeconds(1)))); + + await Assert.ThrowsAsync(() => mngr.AddStateAsync("key1", "value3", token)); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + + // No longer return the value from the state provider. + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("", null))); + + // Key should be expired after 1 seconds. + await Task.Delay(TimeSpan.FromSeconds(1.5)); + await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); + await Assert.ThrowsAsync(() => mngr.RemoveStateAsync("key1", token)); + await mngr.AddStateAsync("key1", "value2", TimeSpan.FromSeconds(1), token); + Assert.Equal("value2", await mngr.GetStateAsync("key1", token)); + } + + [Fact] + public async Task RemoveState() + { + var interactor = new Mock(); + var host = ActorHost.CreateForTest(); + host.StateProvider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var mngr = new ActorStateManager(new TestActor(host)); + var token = new CancellationToken(); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("", null))); + + await Assert.ThrowsAsync(() => mngr.RemoveStateAsync("key1", token)); + + await mngr.AddStateAsync("key1", "value1", token); + await mngr.AddStateAsync("key2", "value2", token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + + await mngr.RemoveStateAsync("key1", token); + await mngr.RemoveStateAsync("key2", token); + + await Assert.ThrowsAsync(() => mngr.GetStateAsync("key1", token)); + await Assert.ThrowsAsync(() => mngr.GetStateAsync("key2", token)); + + // Should be able to add state again after removal. + await mngr.AddStateAsync("key1", "value1", TimeSpan.FromSeconds(1), token); + await mngr.AddStateAsync("key2", "value2", TimeSpan.FromSeconds(1), token); + Assert.Equal("value1", await mngr.GetStateAsync("key1", token)); + Assert.Equal("value2", await mngr.GetStateAsync("key2", token)); + } +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/ActorUnitTestTests.cs b/test/Dapr.Actors.Test/ActorUnitTestTests.cs index 794761998..27f19a775 100644 --- a/test/Dapr.Actors.Test/ActorUnitTestTests.cs +++ b/test/Dapr.Actors.Test/ActorUnitTestTests.cs @@ -19,176 +19,175 @@ using Moq; using Xunit; -namespace Dapr.Actors +namespace Dapr.Actors; + +// Tests that test that we can test... hmmmm... +public class ActorUnitTestTests { - // Tests that test that we can test... hmmmm... - public class ActorUnitTestTests + [Fact] + public async Task CanTestStartingAndStoppingTimer() { - [Fact] - public async Task CanTestStartingAndStoppingTimer() - { - var timers = new List(); - - var timerManager = new Mock(MockBehavior.Strict); - timerManager - .Setup(tm => tm.RegisterTimerAsync(It.IsAny())) - .Callback(timer => timers.Add(timer)) - .Returns(Task.CompletedTask); - timerManager - .Setup(tm => tm.UnregisterTimerAsync(It.IsAny())) - .Callback(timer => timers.RemoveAll(t => t.Name == timer.Name)) - .Returns(Task.CompletedTask); - - var host = ActorHost.CreateForTest(new ActorTestOptions(){ TimerManager = timerManager.Object, }); - var actor = new CoolTestActor(host); - - // Start the timer - var message = new Message() - { - Text = "Remind me to tape the hockey game tonite.", - }; - await actor.StartTimerAsync(message); + var timers = new List(); + + var timerManager = new Mock(MockBehavior.Strict); + timerManager + .Setup(tm => tm.RegisterTimerAsync(It.IsAny())) + .Callback(timer => timers.Add(timer)) + .Returns(Task.CompletedTask); + timerManager + .Setup(tm => tm.UnregisterTimerAsync(It.IsAny())) + .Callback(timer => timers.RemoveAll(t => t.Name == timer.Name)) + .Returns(Task.CompletedTask); + + var host = ActorHost.CreateForTest(new ActorTestOptions(){ TimerManager = timerManager.Object, }); + var actor = new CoolTestActor(host); + + // Start the timer + var message = new Message() + { + Text = "Remind me to tape the hockey game tonite.", + }; + await actor.StartTimerAsync(message); - var timer = Assert.Single(timers); - Assert.Equal("record", timer.Name); - Assert.Equal(TimeSpan.FromSeconds(5), timer.Period); - Assert.Equal(TimeSpan.Zero, timer.DueTime); + var timer = Assert.Single(timers); + Assert.Equal("record", timer.Name); + Assert.Equal(TimeSpan.FromSeconds(5), timer.Period); + Assert.Equal(TimeSpan.Zero, timer.DueTime); - var state = JsonSerializer.Deserialize(timer.Data); - Assert.Equal(message.Text, state.Text); - - // Simulate invoking the callback - for (var i = 0; i < 10; i++) - { - await actor.Tick(timer.Data); - } - - // Stop the timer - await actor.StopTimerAsync(); - Assert.Empty(timers); - } + var state = JsonSerializer.Deserialize(timer.Data); + Assert.Equal(message.Text, state.Text); - [Fact] - public async Task CanTestStartingAndStoppingReminder() + // Simulate invoking the callback + for (var i = 0; i < 10; i++) { - var reminders = new List(); - IActorReminder getReminder = null; - - var timerManager = new Mock(MockBehavior.Strict); - timerManager - .Setup(tm => tm.RegisterReminderAsync(It.IsAny())) - .Callback(reminder => reminders.Add(reminder)) - .Returns(Task.CompletedTask); - timerManager - .Setup(tm => tm.UnregisterReminderAsync(It.IsAny())) - .Callback(reminder => reminders.RemoveAll(t => t.Name == reminder.Name)) - .Returns(Task.CompletedTask); - timerManager - .Setup(tm => tm.GetReminderAsync(It.IsAny())) - .Returns(() => Task.FromResult(getReminder)); - - var host = ActorHost.CreateForTest(new ActorTestOptions(){ TimerManager = timerManager.Object, }); - var actor = new CoolTestActor(host); - - // Start the reminder - var message = new Message() - { - Text = "Remind me to tape the hockey game tonite.", - }; - await actor.StartReminderAsync(message); + await actor.Tick(timer.Data); + } + + // Stop the timer + await actor.StopTimerAsync(); + Assert.Empty(timers); + } + + [Fact] + public async Task CanTestStartingAndStoppingReminder() + { + var reminders = new List(); + IActorReminder getReminder = null; + + var timerManager = new Mock(MockBehavior.Strict); + timerManager + .Setup(tm => tm.RegisterReminderAsync(It.IsAny())) + .Callback(reminder => reminders.Add(reminder)) + .Returns(Task.CompletedTask); + timerManager + .Setup(tm => tm.UnregisterReminderAsync(It.IsAny())) + .Callback(reminder => reminders.RemoveAll(t => t.Name == reminder.Name)) + .Returns(Task.CompletedTask); + timerManager + .Setup(tm => tm.GetReminderAsync(It.IsAny())) + .Returns(() => Task.FromResult(getReminder)); + + var host = ActorHost.CreateForTest(new ActorTestOptions(){ TimerManager = timerManager.Object, }); + var actor = new CoolTestActor(host); + + // Start the reminder + var message = new Message() + { + Text = "Remind me to tape the hockey game tonite.", + }; + await actor.StartReminderAsync(message); - var reminder = Assert.Single(reminders); - Assert.Equal("record", reminder.Name); - Assert.Equal(TimeSpan.FromSeconds(5), reminder.Period); - Assert.Equal(TimeSpan.Zero, reminder.DueTime); + var reminder = Assert.Single(reminders); + Assert.Equal("record", reminder.Name); + Assert.Equal(TimeSpan.FromSeconds(5), reminder.Period); + Assert.Equal(TimeSpan.Zero, reminder.DueTime); - var state = JsonSerializer.Deserialize(reminder.State); - Assert.Equal(message.Text, state.Text); - - // Simulate invoking the reminder interface - for (var i = 0; i < 10; i++) - { - await actor.ReceiveReminderAsync(reminder.Name, reminder.State, reminder.DueTime, reminder.Period); - } - - getReminder = reminder; - var reminderFromGet = await actor.GetReminderAsync(); - Assert.Equal(reminder, reminderFromGet); - - // Stop the reminder - await actor.StopReminderAsync(); - Assert.Empty(reminders); - } + var state = JsonSerializer.Deserialize(reminder.State); + Assert.Equal(message.Text, state.Text); - [Fact] - public async Task ReminderReturnsNullIfNotAvailable() + // Simulate invoking the reminder interface + for (var i = 0; i < 10; i++) { - var timerManager = new Mock(MockBehavior.Strict); - timerManager - .Setup(tm => tm.GetReminderAsync(It.IsAny())) - .Returns(() => Task.FromResult(null)); + await actor.ReceiveReminderAsync(reminder.Name, reminder.State, reminder.DueTime, reminder.Period); + } + + getReminder = reminder; + var reminderFromGet = await actor.GetReminderAsync(); + Assert.Equal(reminder, reminderFromGet); + + // Stop the reminder + await actor.StopReminderAsync(); + Assert.Empty(reminders); + } + + [Fact] + public async Task ReminderReturnsNullIfNotAvailable() + { + var timerManager = new Mock(MockBehavior.Strict); + timerManager + .Setup(tm => tm.GetReminderAsync(It.IsAny())) + .Returns(() => Task.FromResult(null)); - var host = ActorHost.CreateForTest(new ActorTestOptions() { TimerManager = timerManager.Object, }); - var actor = new CoolTestActor(host); + var host = ActorHost.CreateForTest(new ActorTestOptions() { TimerManager = timerManager.Object, }); + var actor = new CoolTestActor(host); - //There is no starting reminder, so this should always return null - var retrievedReminder = await actor.GetReminderAsync(); - Assert.Null(retrievedReminder); + //There is no starting reminder, so this should always return null + var retrievedReminder = await actor.GetReminderAsync(); + Assert.Null(retrievedReminder); + } + + public interface ICoolTestActor : IActor + { + } + + public class Message + { + public string Text { get; set; } + public bool IsImportant { get; set; } + } + + public class CoolTestActor : Actor, ICoolTestActor, IRemindable + { + public CoolTestActor(ActorHost host) + : base(host) + { + } + + public async Task StartTimerAsync(Message message) + { + var bytes = JsonSerializer.SerializeToUtf8Bytes(message); + await this.RegisterTimerAsync("record", nameof(Tick), bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(5)); + } + + public async Task StopTimerAsync() + { + await this.UnregisterTimerAsync("record"); + } + + public async Task StartReminderAsync(Message message) + { + var bytes = JsonSerializer.SerializeToUtf8Bytes(message); + await this.RegisterReminderAsync("record", bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(5)); + } + + public async Task GetReminderAsync() + { + return await this.GetReminderAsync("record"); } - public interface ICoolTestActor : IActor + public async Task StopReminderAsync() { + await this.UnregisterReminderAsync("record"); } - public class Message + public Task Tick(byte[] data) { - public string Text { get; set; } - public bool IsImportant { get; set; } + return Task.CompletedTask; } - public class CoolTestActor : Actor, ICoolTestActor, IRemindable + public Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) { - public CoolTestActor(ActorHost host) - : base(host) - { - } - - public async Task StartTimerAsync(Message message) - { - var bytes = JsonSerializer.SerializeToUtf8Bytes(message); - await this.RegisterTimerAsync("record", nameof(Tick), bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(5)); - } - - public async Task StopTimerAsync() - { - await this.UnregisterTimerAsync("record"); - } - - public async Task StartReminderAsync(Message message) - { - var bytes = JsonSerializer.SerializeToUtf8Bytes(message); - await this.RegisterReminderAsync("record", bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(5)); - } - - public async Task GetReminderAsync() - { - return await this.GetReminderAsync("record"); - } - - public async Task StopReminderAsync() - { - await this.UnregisterReminderAsync("record"); - } - - public Task Tick(byte[] data) - { - return Task.CompletedTask; - } - - public Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) - { - return Task.CompletedTask; - } + return Task.CompletedTask; } } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/ApiTokenTests.cs b/test/Dapr.Actors.Test/ApiTokenTests.cs index 6e5860be2..5eae4c8b9 100644 --- a/test/Dapr.Actors.Test/ApiTokenTests.cs +++ b/test/Dapr.Actors.Test/ApiTokenTests.cs @@ -18,100 +18,99 @@ using Shouldly; using Xunit; -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +public class ApiTokenTests { - public class ApiTokenTests + [Fact(Skip = "https://github.com/dapr/dotnet-sdk/issues/596")] + public async Task CreateProxyWithRemoting_WithApiToken() { - [Fact(Skip = "https://github.com/dapr/dotnet-sdk/issues/596")] - public async Task CreateProxyWithRemoting_WithApiToken() + await using var client = TestClient.CreateForMessageHandler(); + + var actorId = new ActorId("abc"); + var options = new ActorProxyOptions + { + DaprApiToken = "test_token", + }; + + var request = await client.CaptureHttpRequestAsync(async handler => { - await using var client = TestClient.CreateForMessageHandler(); + var factory = new ActorProxyFactory(options, handler); + var proxy = factory.CreateActorProxy(actorId, "TestActor"); + await proxy.SetCountAsync(1, new CancellationToken()); + }); - var actorId = new ActorId("abc"); - var options = new ActorProxyOptions - { - DaprApiToken = "test_token", - }; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async handler => - { - var factory = new ActorProxyFactory(options, handler); - var proxy = factory.CreateActorProxy(actorId, "TestActor"); - await proxy.SetCountAsync(1, new CancellationToken()); - }); + var headerValues = request.Request.Headers.GetValues("dapr-api-token"); + headerValues.ShouldContain("test_token"); + } - request.Dismiss(); + [Fact(Skip = "https://github.com/dapr/dotnet-sdk/issues/596")] + public async Task CreateProxyWithRemoting_WithNoApiToken() + { + await using var client = TestClient.CreateForMessageHandler(); - var headerValues = request.Request.Headers.GetValues("dapr-api-token"); - headerValues.ShouldContain("test_token"); - } + var actorId = new ActorId("abc"); - [Fact(Skip = "https://github.com/dapr/dotnet-sdk/issues/596")] - public async Task CreateProxyWithRemoting_WithNoApiToken() + var request = await client.CaptureHttpRequestAsync(async handler => { - await using var client = TestClient.CreateForMessageHandler(); + var factory = new ActorProxyFactory(null, handler); + var proxy = factory.CreateActorProxy(actorId, "TestActor"); + await proxy.SetCountAsync(1, new CancellationToken()); + }); - var actorId = new ActorId("abc"); + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async handler => - { - var factory = new ActorProxyFactory(null, handler); - var proxy = factory.CreateActorProxy(actorId, "TestActor"); - await proxy.SetCountAsync(1, new CancellationToken()); - }); + Assert.Throws(() => + { + request.Request.Headers.GetValues("dapr-api-token"); + }); + } - request.Dismiss(); + [Fact] + public async Task CreateProxyWithNoRemoting_WithApiToken() + { + await using var client = TestClient.CreateForMessageHandler(); - Assert.Throws(() => - { - request.Request.Headers.GetValues("dapr-api-token"); - }); - } + var actorId = new ActorId("abc"); + var options = new ActorProxyOptions + { + DaprApiToken = "test_token", + }; - [Fact] - public async Task CreateProxyWithNoRemoting_WithApiToken() + var request = await client.CaptureHttpRequestAsync(async handler => { - await using var client = TestClient.CreateForMessageHandler(); + var factory = new ActorProxyFactory(options, handler); + var proxy = factory.Create(actorId, "TestActor"); + await proxy.InvokeMethodAsync("SetCountAsync", 1, new CancellationToken()); + }); - var actorId = new ActorId("abc"); - var options = new ActorProxyOptions - { - DaprApiToken = "test_token", - }; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async handler => - { - var factory = new ActorProxyFactory(options, handler); - var proxy = factory.Create(actorId, "TestActor"); - await proxy.InvokeMethodAsync("SetCountAsync", 1, new CancellationToken()); - }); + var headerValues = request.Request.Headers.GetValues("dapr-api-token"); + headerValues.ShouldContain("test_token"); + } - request.Dismiss(); + [Fact] + public async Task CreateProxyWithNoRemoting_WithNoApiToken() + { + await using var client = TestClient.CreateForMessageHandler(); - var headerValues = request.Request.Headers.GetValues("dapr-api-token"); - headerValues.ShouldContain("test_token"); - } + var actorId = new ActorId("abc"); - [Fact] - public async Task CreateProxyWithNoRemoting_WithNoApiToken() + var request = await client.CaptureHttpRequestAsync(async handler => { - await using var client = TestClient.CreateForMessageHandler(); + var factory = new ActorProxyFactory(null, handler); + var proxy = factory.Create(actorId, "TestActor"); + await proxy.InvokeMethodAsync("SetCountAsync", 1, new CancellationToken()); + }); - var actorId = new ActorId("abc"); + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async handler => - { - var factory = new ActorProxyFactory(null, handler); - var proxy = factory.Create(actorId, "TestActor"); - await proxy.InvokeMethodAsync("SetCountAsync", 1, new CancellationToken()); - }); - - request.Dismiss(); - - Assert.Throws(() => - { - request.Request.Headers.GetValues("dapr-api-token"); - }); - } + Assert.Throws(() => + { + request.Request.Headers.GetValues("dapr-api-token"); + }); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs b/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs index 9d44e7550..ed493e8ab 100644 --- a/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs +++ b/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs @@ -12,145 +12,144 @@ // ------------------------------------------------------------------------ #pragma warning disable 0618 -namespace Dapr.Actors.Test -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Threading.Tasks; - using Dapr.Actors.Runtime; - using Newtonsoft.Json; - using Xunit; +namespace Dapr.Actors.Test; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Dapr.Actors.Runtime; +using Newtonsoft.Json; +using Xunit; - public class DaprFormatTimeSpanTests +public class DaprFormatTimeSpanTests +{ + public static readonly IEnumerable DaprFormatTimeSpanJsonStringsAndExpectedDeserializedValues = new List { - public static readonly IEnumerable DaprFormatTimeSpanJsonStringsAndExpectedDeserializedValues = new List + new object[] + { + "{\"dueTime\":\"4h15m50s60ms\"", + new TimeSpan(0, 4, 15, 50, 60), + }, + new object[] { - new object[] - { - "{\"dueTime\":\"4h15m50s60ms\"", - new TimeSpan(0, 4, 15, 50, 60), - }, - new object[] - { - "{\"dueTime\":\"0h35m10s12ms\"", - new TimeSpan(0, 0, 35, 10, 12), - }, - }; + "{\"dueTime\":\"0h35m10s12ms\"", + new TimeSpan(0, 0, 35, 10, 12), + }, + }; - public static readonly IEnumerable DaprReminderISO8601FormatTimeSpanAndExpectedDeserializedValues = new List + public static readonly IEnumerable DaprReminderISO8601FormatTimeSpanAndExpectedDeserializedValues = new List + { + new object[] { - new object[] - { - "R10/PT10S", - new TimeSpan(0, 0, 0, 10, 0), - 10 - }, - new object[] - { - "PT10H5M10S", - new TimeSpan(0, 10, 5, 10, 0), - null, - }, - new object[] - { - "P1Y1M1W1DT1H1M1S", - new TimeSpan(403, 1, 1, 1, 0), - null, - }, - new object[] - { - "R3/P2W3DT4H59M", - new TimeSpan(17, 4, 59, 0, 0), - 3, - } - }; - - public static readonly IEnumerable DaprReminderTimeSpanToDaprISO8601Format = new List + "R10/PT10S", + new TimeSpan(0, 0, 0, 10, 0), + 10 + }, + new object[] { - new object[] - { - new TimeSpan(10, 10, 10, 10), - 1, - "R1/P10DT10H10M10S" - }, - new object[] - { - new TimeSpan(17, 4, 59, 0, 0), - 3, - "R3/P17DT4H59M" - }, - new object[] - { - new TimeSpan(0, 7, 23, 12, 0), - null, - "7h23m12s0ms" - } - }; - - [Theory] - [MemberData(nameof(DaprFormatTimeSpanJsonStringsAndExpectedDeserializedValues))] - public void DaprFormat_TimeSpan_Parsing(string daprFormatTimeSpanJsonString, TimeSpan expectedDeserializedValue) + "PT10H5M10S", + new TimeSpan(0, 10, 5, 10, 0), + null, + }, + new object[] + { + "P1Y1M1W1DT1H1M1S", + new TimeSpan(403, 1, 1, 1, 0), + null, + }, + new object[] { - using var textReader = new StringReader(daprFormatTimeSpanJsonString); - using var jsonTextReader = new JsonTextReader(textReader); + "R3/P2W3DT4H59M", + new TimeSpan(17, 4, 59, 0, 0), + 3, + } + }; - while (jsonTextReader.TokenType != JsonToken.String) - { - jsonTextReader.Read(); - } + public static readonly IEnumerable DaprReminderTimeSpanToDaprISO8601Format = new List + { + new object[] + { + new TimeSpan(10, 10, 10, 10), + 1, + "R1/P10DT10H10M10S" + }, + new object[] + { + new TimeSpan(17, 4, 59, 0, 0), + 3, + "R3/P17DT4H59M" + }, + new object[] + { + new TimeSpan(0, 7, 23, 12, 0), + null, + "7h23m12s0ms" + } + }; - var timespanString = (string)jsonTextReader.Value; - var deserializedTimeSpan = ConverterUtils.ConvertTimeSpanFromDaprFormat(timespanString); + [Theory] + [MemberData(nameof(DaprFormatTimeSpanJsonStringsAndExpectedDeserializedValues))] + public void DaprFormat_TimeSpan_Parsing(string daprFormatTimeSpanJsonString, TimeSpan expectedDeserializedValue) + { + using var textReader = new StringReader(daprFormatTimeSpanJsonString); + using var jsonTextReader = new JsonTextReader(textReader); - Assert.Equal(expectedDeserializedValue, deserializedTimeSpan); + while (jsonTextReader.TokenType != JsonToken.String) + { + jsonTextReader.Read(); } - [Fact] - public async Task ConverterUtilsTestEndToEndAsync() - { - static Task SerializeAsync(TimeSpan dueTime, TimeSpan period) - { - var timerInfo = new TimerInfo( - callback: null, - state: null, - dueTime: dueTime, - period: period); - return Task.FromResult(System.Text.Json.JsonSerializer.Serialize(timerInfo)); - } + var timespanString = (string)jsonTextReader.Value; + var deserializedTimeSpan = ConverterUtils.ConvertTimeSpanFromDaprFormat(timespanString); - var inTheFuture = TimeSpan.FromMilliseconds(20); - var never = TimeSpan.FromMilliseconds(-1); - Assert.Equal( - "{\"dueTime\":\"0h0m0s20ms\"}", - await SerializeAsync(inTheFuture, never)); - } + Assert.Equal(expectedDeserializedValue, deserializedTimeSpan); + } - [Fact] - public void DaprFormatTimespanEmpty() + [Fact] + public async Task ConverterUtilsTestEndToEndAsync() + { + static Task SerializeAsync(TimeSpan dueTime, TimeSpan period) { - Func convert = ConverterUtils.ConvertTimeSpanFromDaprFormat; - TimeSpan never = TimeSpan.FromMilliseconds(-1); - Assert.Equal(never, convert(null)); - Assert.Equal(never, convert(string.Empty)); + var timerInfo = new TimerInfo( + callback: null, + state: null, + dueTime: dueTime, + period: period); + return Task.FromResult(System.Text.Json.JsonSerializer.Serialize(timerInfo)); } + + var inTheFuture = TimeSpan.FromMilliseconds(20); + var never = TimeSpan.FromMilliseconds(-1); + Assert.Equal( + "{\"dueTime\":\"0h0m0s20ms\"}", + await SerializeAsync(inTheFuture, never)); + } + + [Fact] + public void DaprFormatTimespanEmpty() + { + Func convert = ConverterUtils.ConvertTimeSpanFromDaprFormat; + TimeSpan never = TimeSpan.FromMilliseconds(-1); + Assert.Equal(never, convert(null)); + Assert.Equal(never, convert(string.Empty)); + } - [Theory] - [MemberData(nameof(DaprReminderISO8601FormatTimeSpanAndExpectedDeserializedValues))] - public void DaprReminderFormat_TimeSpan_Parsing(string valueString, TimeSpan expectedDuration, int? expectedRepetition) - { - (TimeSpan duration, int? repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(valueString); - Assert.Equal(expectedDuration, duration); - Assert.Equal(expectedRepetition, repetition); - } + [Theory] + [MemberData(nameof(DaprReminderISO8601FormatTimeSpanAndExpectedDeserializedValues))] + public void DaprReminderFormat_TimeSpan_Parsing(string valueString, TimeSpan expectedDuration, int? expectedRepetition) + { + (TimeSpan duration, int? repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(valueString); + Assert.Equal(expectedDuration, duration); + Assert.Equal(expectedRepetition, repetition); + } - [Theory] - [MemberData(nameof(DaprReminderTimeSpanToDaprISO8601Format))] - public void DaprReminderFormat_ConvertFromTimeSpan_ToDaprFormat(TimeSpan period, int? repetitions, string expectedValue) - { - var actualValue = ConverterUtils.ConvertTimeSpanValueInISO8601Format(period, repetitions); - Assert.Equal(expectedValue, actualValue); - } + [Theory] + [MemberData(nameof(DaprReminderTimeSpanToDaprISO8601Format))] + public void DaprReminderFormat_ConvertFromTimeSpan_ToDaprFormat(TimeSpan period, int? repetitions, string expectedValue) + { + var actualValue = ConverterUtils.ConvertTimeSpanValueInISO8601Format(period, repetitions); + Assert.Equal(expectedValue, actualValue); } } #pragma warning restore 0618 diff --git a/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs b/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs index 30ddb91d7..35841f0a4 100644 --- a/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs +++ b/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs @@ -11,421 +11,420 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Authentication; +using System.Text.Json; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +/// +/// Contains tests for DaprHttpInteractor. +/// +public class DaprHttpInteractorTest { - using System; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Security.Authentication; - using System.Text.Json; - using System.Threading.Tasks; - using Shouldly; - using Xunit; - - /// - /// Contains tests for DaprHttpInteractor. - /// - public class DaprHttpInteractorTest + [Fact] + public async Task GetState_ValidateRequest() { - [Fact] - public async Task GetState_ValidateRequest() + await using var client = TestClient.CreateForDaprHttpInterator(); + + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string keyName = "StateKey_Test"; + + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.GetStateAsync(actorType, actorId, keyName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string keyName = "StateKey_Test"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.GetStateAsync(actorType, actorId, keyName); - }); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName); - request.Dismiss(); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Get); + } - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName); + [Fact] + public async Task SaveStateTransactionally_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Get); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string data = "StateData"; - [Fact] - public async Task SaveStateTransactionally_ValidateRequest() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.SaveStateTransactionallyAsync(actorType, actorId, data); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string data = "StateData"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.SaveStateTransactionallyAsync(actorType, actorId, data); - }); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId); - request.Dismiss(); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); + } - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId); + [Fact] + public async Task InvokeActorMethodWithoutRemoting_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Put); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string methodName = "MethodName"; + const string payload = "JsonData"; - [Fact] - public async Task InvokeActorMethodWithoutRemoting_ValidateRequest() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, payload); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string methodName = "MethodName"; - const string payload = "JsonData"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, payload); - }); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); - request.Dismiss(); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); + } - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); + [Fact] + public async Task RegisterReminder_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Put); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string reminderName = "ReminderName"; + const string payload = "JsonData"; - [Fact] - public async Task RegisterReminder_ValidateRequest() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.RegisterReminderAsync(actorType, actorId, reminderName, payload); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string reminderName = "ReminderName"; - const string payload = "JsonData"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.RegisterReminderAsync(actorType, actorId, reminderName, payload); - }); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); - request.Dismiss(); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); + } - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); + [Fact] + public async Task UnregisterReminder_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Put); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string reminderName = "ReminderName"; - [Fact] - public async Task UnregisterReminder_ValidateRequest() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.UnregisterReminderAsync(actorType, actorId, reminderName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string reminderName = "ReminderName"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.UnregisterReminderAsync(actorType, actorId, reminderName); - }); - - request.Dismiss(); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Delete); + } - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Delete); - } + [Fact] + public async Task GetReminder_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); + + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string reminderName = "ReminderName"; - [Fact] - public async Task GetReminder_ValidateRequest() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); - - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string reminderName = "ReminderName"; + await httpInteractor.GetReminderAsync(actorType, actorId, reminderName); + }); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.GetReminderAsync(actorType, actorId, reminderName); - }); + request.Dismiss(); - request.Dismiss(); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, + actorType, actorId, reminderName); - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, - actorType, actorId, reminderName); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Get); + } - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Get); - } + [Fact] + public async Task RegisterTimer_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - [Fact] - public async Task RegisterTimer_ValidateRequest() + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; + const string payload = "JsonData"; + + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.RegisterTimerAsync(actorType, actorId, timerName, payload); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string timerName = "TimerName"; - const string payload = "JsonData"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.RegisterTimerAsync(actorType, actorId, timerName, payload); - }); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); - request.Dismiss(); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); + } - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); + [Fact] + public async Task UnregisterTimer_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Put); - } + var actorType = "ActorType_Test"; + var actorId = "ActorId_Test"; + var timerName = "TimerName"; - [Fact] - public async Task UnregisterTimer_ValidateRequest() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); + }); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var timerName = "TimerName"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); - }); + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); - request.Dismiss(); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Delete); + } - var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); - var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); + [Fact] + public async Task Call_WithApiTokenSet() + { + await using var client = TestClient.CreateForDaprHttpInterator(apiToken: "test_token"); - actualPath.ShouldBe(expectedPath); - request.Request.Method.ShouldBe(HttpMethod.Delete); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; - [Fact] - public async Task Call_WithApiTokenSet() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(apiToken: "test_token"); + await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string timerName = "TimerName"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); - }); + request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); + headerValues.Count().ShouldBe(1); + headerValues.First().ShouldBe("test_token"); + } - request.Dismiss(); + [Fact] + public async Task Call_WithoutApiToken() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.Count().ShouldBe(1); - headerValues.First().ShouldBe("test_token"); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; - [Fact] - public async Task Call_WithoutApiToken() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string timerName = "TimerName"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); - }); + request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); + headerValues.ShouldBeNull(); + } - request.Dismiss(); + [Fact] + public async Task Call_ValidateUnsuccessfulResponse() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.ShouldBeNull(); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; - [Fact] - public async Task Call_ValidateUnsuccessfulResponse() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string timerName = "TimerName"; + request.Dismiss(); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); - }); + var error = new DaprError() + { + ErrorCode = "ERR_STATE_STORE", + Message = "State Store Error" + }; - request.Dismiss(); + var message = new HttpResponseMessage(HttpStatusCode.InternalServerError) + { + Content = new StringContent(JsonSerializer.Serialize(error)) + }; - var error = new DaprError() - { - ErrorCode = "ERR_STATE_STORE", - Message = "State Store Error" - }; + await Assert.ThrowsAsync(async () => + { + await request.CompleteAsync(message); + }); + } - var message = new HttpResponseMessage(HttpStatusCode.InternalServerError) - { - Content = new StringContent(JsonSerializer.Serialize(error)) - }; + [Fact] + public async Task Call_ValidateUnsuccessful404Response() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(message); - }); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; - [Fact] - public async Task Call_ValidateUnsuccessful404Response() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string timerName = "TimerName"; + var message = new HttpResponseMessage(HttpStatusCode.NotFound); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); - }); + await Assert.ThrowsAsync(async () => + { + await request.CompleteAsync(message); + }); + } - var message = new HttpResponseMessage(HttpStatusCode.NotFound); + [Fact] + public async Task Call_ValidateUnauthorizedResponse() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(message); - }); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; - [Fact] - public async Task Call_ValidateUnauthorizedResponse() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string timerName = "TimerName"; + var message = new HttpResponseMessage(HttpStatusCode.Unauthorized); - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.UnregisterTimerAsync(actorType, actorId, timerName); - }); + await Assert.ThrowsAsync(async () => + { + await request.CompleteAsync(message); + }); + } - var message = new HttpResponseMessage(HttpStatusCode.Unauthorized); + [Fact] + public async Task InvokeActorMethodAddsReentrancyIdIfSet_ValidateHeaders() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(message); - }); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string methodName = "MethodName"; + const string payload = "JsonData"; - [Fact] - public async Task InvokeActorMethodAddsReentrancyIdIfSet_ValidateHeaders() + ActorReentrancyContextAccessor.ReentrancyContext = "1"; + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, payload); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string methodName = "MethodName"; - const string payload = "JsonData"; + request.Dismiss(); + Assert.True(request.Request.Headers.Contains(Constants.ReentrancyRequestHeaderName)); + Assert.Contains("1", request.Request.Headers.GetValues(Constants.ReentrancyRequestHeaderName)); + } - ActorReentrancyContextAccessor.ReentrancyContext = "1"; - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, payload); - }); + [Fact] + public async Task InvokeActorMethodOmitsReentrancyIdIfNotSet_ValidateHeaders() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - request.Dismiss(); - Assert.True(request.Request.Headers.Contains(Constants.ReentrancyRequestHeaderName)); - Assert.Contains("1", request.Request.Headers.GetValues(Constants.ReentrancyRequestHeaderName)); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string methodName = "MethodName"; + const string payload = "JsonData"; - [Fact] - public async Task InvokeActorMethodOmitsReentrancyIdIfNotSet_ValidateHeaders() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + await httpInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, payload); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string methodName = "MethodName"; - const string payload = "JsonData"; + request.Dismiss(); + Assert.False(request.Request.Headers.Contains(Constants.ReentrancyRequestHeaderName)); + } - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - await httpInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, payload); - }); + [Fact] + public async Task GetState_TTLExpireTimeExists() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - request.Dismiss(); - Assert.False(request.Request.Headers.Contains(Constants.ReentrancyRequestHeaderName)); - } + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string keyName = "StateKey_Test"; - [Fact] - public async Task GetState_TTLExpireTimeExists() + var request = await client.CaptureHttpRequestAsync(async httpInteractor => { - await using var client = TestClient.CreateForDaprHttpInterator(); + return await httpInteractor.GetStateAsync(actorType, actorId, keyName); + }); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string keyName = "StateKey_Test"; - - var request = await client.CaptureHttpRequestAsync(async httpInteractor => + var message = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("test"), + Headers = { - return await httpInteractor.GetStateAsync(actorType, actorId, keyName); - }); + { "Metadata.ttlExpireTime", "2023-04-05T23:22:21Z" }, + }, + }; + + var actual = await request.CompleteAsync(message); + Assert.Equal("test", actual.Value); + var expTTL = new DateTimeOffset(2023, 04, 05, 23, 22, 21, 0, new GregorianCalendar(), new TimeSpan(0, 0, 0)); + Assert.Equal(expTTL, actual.TTLExpireTime); + } - var message = new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent("test"), - Headers = - { - { "Metadata.ttlExpireTime", "2023-04-05T23:22:21Z" }, - }, - }; - - var actual = await request.CompleteAsync(message); - Assert.Equal("test", actual.Value); - var expTTL = new DateTimeOffset(2023, 04, 05, 23, 22, 21, 0, new GregorianCalendar(), new TimeSpan(0, 0, 0)); - Assert.Equal(expTTL, actual.TTLExpireTime); - } - - [Fact] - public async Task GetState_TTLExpireTimeNotExists() - { - await using var client = TestClient.CreateForDaprHttpInterator(); + [Fact] + public async Task GetState_TTLExpireTimeNotExists() + { + await using var client = TestClient.CreateForDaprHttpInterator(); - const string actorType = "ActorType_Test"; - const string actorId = "ActorId_Test"; - const string keyName = "StateKey_Test"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string keyName = "StateKey_Test"; - var request = await client.CaptureHttpRequestAsync(async httpInteractor => - { - return await httpInteractor.GetStateAsync(actorType, actorId, keyName); - }); + var request = await client.CaptureHttpRequestAsync(async httpInteractor => + { + return await httpInteractor.GetStateAsync(actorType, actorId, keyName); + }); - var message = new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent("test"), - }; + var message = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("test"), + }; - var actual = await request.CompleteAsync(message); - Assert.Equal("test", actual.Value); - Assert.False(actual.TTLExpireTime.HasValue); - } + var actual = await request.CompleteAsync(message); + Assert.Equal("test", actual.Value); + Assert.False(actual.TTLExpireTime.HasValue); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/DaprStateProviderTest.cs b/test/Dapr.Actors.Test/DaprStateProviderTest.cs index 948b14b46..5c6839f72 100644 --- a/test/Dapr.Actors.Test/DaprStateProviderTest.cs +++ b/test/Dapr.Actors.Test/DaprStateProviderTest.cs @@ -11,120 +11,119 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using Xunit; +using Dapr.Actors.Communication; +using Dapr.Actors.Runtime; +using Moq; + +/// +/// Contains tests for DaprStateProvider. +/// +public class DaprStateProviderTest { - using System; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using System.Collections.Generic; - using Xunit; - using Dapr.Actors.Communication; - using Dapr.Actors.Runtime; - using Moq; - - /// - /// Contains tests for DaprStateProvider. - /// - public class DaprStateProviderTest + [Fact] + public async Task SaveStateAsync() { - [Fact] - public async Task SaveStateAsync() - { - var interactor = new Mock(); - var provider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var token = new CancellationToken(); - - var stateChangeList = new List(); - stateChangeList.Add( - new ActorStateChange("key1", typeof(string), "value1", StateChangeKind.Add, DateTimeOffset.UtcNow.Add(TimeSpan.FromSeconds(2)))); - stateChangeList.Add( - new ActorStateChange("key2", typeof(string), "value2", StateChangeKind.Add, null)); - - string content = null; - interactor - .Setup(d => d.SaveStateTransactionallyAsync( + var interactor = new Mock(); + var provider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var token = new CancellationToken(); + + var stateChangeList = new List(); + stateChangeList.Add( + new ActorStateChange("key1", typeof(string), "value1", StateChangeKind.Add, DateTimeOffset.UtcNow.Add(TimeSpan.FromSeconds(2)))); + stateChangeList.Add( + new ActorStateChange("key2", typeof(string), "value2", StateChangeKind.Add, null)); + + string content = null; + interactor + .Setup(d => d.SaveStateTransactionallyAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((actorType, actorId, data, token) => content = data) - .Returns(Task.FromResult(true)); - - await provider.SaveStateAsync("actorType", "actorId", stateChangeList, token); - Assert.Equal( - "[{\"operation\":\"upsert\",\"request\":{\"key\":\"key1\",\"value\":\"value1\",\"metadata\":{\"ttlInSeconds\":\"2\"}}},{\"operation\":\"upsert\",\"request\":{\"key\":\"key2\",\"value\":\"value2\"}}]", - content - ); - } - - [Fact] - public async Task ContainsStateAsync() - { - var interactor = new Mock(); - var provider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var token = new CancellationToken(); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("", null))); - Assert.False(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("\"value\"", null))); - Assert.True(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); - - var ttl = DateTime.UtcNow.AddSeconds(1); - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); - Assert.True(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); - - ttl = DateTime.UtcNow.AddSeconds(-1); - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); - Assert.False(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); - } - - [Fact] - public async Task TryLoadStateAsync() - { - var interactor = new Mock(); - var provider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); - var token = new CancellationToken(); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("", null))); - var resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); - Assert.False(resp.HasValue); - - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("\"value\"", null))); - resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); - Assert.True(resp.HasValue); - Assert.Equal("value", resp.Value.Value); - Assert.False(resp.Value.TTLExpireTime.HasValue); - - var ttl = DateTime.UtcNow.AddSeconds(1); - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); - resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); - Assert.True(resp.HasValue); - Assert.Equal("value", resp.Value.Value); - Assert.True(resp.Value.TTLExpireTime.HasValue); - Assert.Equal(ttl, resp.Value.TTLExpireTime.Value); - - ttl = DateTime.UtcNow.AddSeconds(-1); - interactor - .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); - resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); - Assert.False(resp.HasValue); - } + .Callback((actorType, actorId, data, token) => content = data) + .Returns(Task.FromResult(true)); + + await provider.SaveStateAsync("actorType", "actorId", stateChangeList, token); + Assert.Equal( + "[{\"operation\":\"upsert\",\"request\":{\"key\":\"key1\",\"value\":\"value1\",\"metadata\":{\"ttlInSeconds\":\"2\"}}},{\"operation\":\"upsert\",\"request\":{\"key\":\"key2\",\"value\":\"value2\"}}]", + content + ); + } + + [Fact] + public async Task ContainsStateAsync() + { + var interactor = new Mock(); + var provider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var token = new CancellationToken(); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("", null))); + Assert.False(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("\"value\"", null))); + Assert.True(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); + + var ttl = DateTime.UtcNow.AddSeconds(1); + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); + Assert.True(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); + + ttl = DateTime.UtcNow.AddSeconds(-1); + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); + Assert.False(await provider.ContainsStateAsync("actorType", "actorId", "key", token)); + } + + [Fact] + public async Task TryLoadStateAsync() + { + var interactor = new Mock(); + var provider = new DaprStateProvider(interactor.Object, new JsonSerializerOptions()); + var token = new CancellationToken(); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("", null))); + var resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); + Assert.False(resp.HasValue); + + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("\"value\"", null))); + resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); + Assert.True(resp.HasValue); + Assert.Equal("value", resp.Value.Value); + Assert.False(resp.Value.TTLExpireTime.HasValue); + + var ttl = DateTime.UtcNow.AddSeconds(1); + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); + resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); + Assert.True(resp.HasValue); + Assert.Equal("value", resp.Value.Value); + Assert.True(resp.Value.TTLExpireTime.HasValue); + Assert.Equal(ttl, resp.Value.TTLExpireTime.Value); + + ttl = DateTime.UtcNow.AddSeconds(-1); + interactor + .Setup(d => d.GetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new ActorStateResponse("\"value\"", ttl))); + resp = await provider.TryLoadStateAsync("actorType", "actorId", "key", token); + Assert.False(resp.HasValue); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs b/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs index 3193a7957..082000e53 100644 --- a/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs @@ -16,145 +16,144 @@ using Shouldly; using Xunit; -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +public sealed class ActorInterfaceDescriptionTests { - public sealed class ActorInterfaceDescriptionTests + [Fact] + public void ActorInterfaceDescription_CreateActorInterfaceDescription() { - [Fact] - public void ActorInterfaceDescription_CreateActorInterfaceDescription() - { - // Arrange - Type type = typeof(ITestActor); + // Arrange + Type type = typeof(ITestActor); - // Act - var description = ActorInterfaceDescription.Create(type); + // Act + var description = ActorInterfaceDescription.Create(type); - // Assert - description.ShouldNotBeNull(); + // Assert + description.ShouldNotBeNull(); - description.InterfaceType.ShouldBe(type); - description.Id.ShouldNotBe(0); - description.V1Id.ShouldBe(0); - description.Methods.Length.ShouldBe(2); - } - - [Fact] - public void ActorInterfaceDescription_CreateThrowsArgumentException_WhenTypeIsNotAnInterface() - { - // Arrange - Type type = typeof(object); - - // Act - Action action = () => ActorInterfaceDescription.Create(type); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void ActorInterfaceDescription_CreateThrowsArgumentException_WhenTypeIsNotAnActorInterface() - { - // Arrange - Type type = typeof(ICloneable); - - // Act - Action action = () => ActorInterfaceDescription.Create(type); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void ActorInterfaceDescription_CreateThrowsArgumentException_WhenActorInterfaceInheritsNonActorInterfaces() - { - // Arrange - Type type = typeof(IClonableActor); - - // Act - Action action = () => ActorInterfaceDescription.Create(type); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void ActorInterfaceDescription_CreateUsingCRCIdActorInterfaceDescription() - { - // Arrange - Type type = typeof(ITestActor); - - // Act - var description = ActorInterfaceDescription.CreateUsingCRCId(type); - - // Assert - description.ShouldNotBeNull(); - - description.InterfaceType.ShouldBe(type); - description.Id.ShouldBe(-934188464); - description.V1Id.ShouldNotBe(0); - description.Methods.Length.ShouldBe(2); - } - - [Fact] - public void ActorInterfaceDescription_CreateUsingCRCIdThrowsArgumentException_WhenTypeIsNotAnInterface() - { - // Arrange - Type type = typeof(object); - - // Act - Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void ActorInterfaceDescription_CreateUsingCRCIdThrowsArgumentException_WhenTypeIsNotAnActorInterface() - { - // Arrange - Type type = typeof(ICloneable); - - // Act - Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void ActorInterfaceDescription_CreateUsingCRCIdThrowsArgumentException_WhenActorInterfaceInheritsNonActorInterfaces() - { - // Arrange - Type type = typeof(IClonableActor); - - // Act - Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - internal interface IClonableActor : ICloneable, IActor - { - } - - internal interface ITestActor : IActor - { - Task GetString(); - - Task MethodWithArguments(int number, bool choice, string information); - } + description.InterfaceType.ShouldBe(type); + description.Id.ShouldNotBe(0); + description.V1Id.ShouldBe(0); + description.Methods.Length.ShouldBe(2); + } + + [Fact] + public void ActorInterfaceDescription_CreateThrowsArgumentException_WhenTypeIsNotAnInterface() + { + // Arrange + Type type = typeof(object); + + // Act + Action action = () => ActorInterfaceDescription.Create(type); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void ActorInterfaceDescription_CreateThrowsArgumentException_WhenTypeIsNotAnActorInterface() + { + // Arrange + Type type = typeof(ICloneable); + + // Act + Action action = () => ActorInterfaceDescription.Create(type); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void ActorInterfaceDescription_CreateThrowsArgumentException_WhenActorInterfaceInheritsNonActorInterfaces() + { + // Arrange + Type type = typeof(IClonableActor); + + // Act + Action action = () => ActorInterfaceDescription.Create(type); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void ActorInterfaceDescription_CreateUsingCRCIdActorInterfaceDescription() + { + // Arrange + Type type = typeof(ITestActor); + + // Act + var description = ActorInterfaceDescription.CreateUsingCRCId(type); + + // Assert + description.ShouldNotBeNull(); + + description.InterfaceType.ShouldBe(type); + description.Id.ShouldBe(-934188464); + description.V1Id.ShouldNotBe(0); + description.Methods.Length.ShouldBe(2); + } + + [Fact] + public void ActorInterfaceDescription_CreateUsingCRCIdThrowsArgumentException_WhenTypeIsNotAnInterface() + { + // Arrange + Type type = typeof(object); + + // Act + Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void ActorInterfaceDescription_CreateUsingCRCIdThrowsArgumentException_WhenTypeIsNotAnActorInterface() + { + // Arrange + Type type = typeof(ICloneable); + + // Act + Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void ActorInterfaceDescription_CreateUsingCRCIdThrowsArgumentException_WhenActorInterfaceInheritsNonActorInterfaces() + { + // Arrange + Type type = typeof(IClonableActor); + + // Act + Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + internal interface IClonableActor : ICloneable, IActor + { + } + + internal interface ITestActor : IActor + { + Task GetString(); + + Task MethodWithArguments(int number, bool choice, string information); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs b/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs index 83f4de018..e0fd5f99c 100644 --- a/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs @@ -17,239 +17,238 @@ using Shouldly; using Xunit; -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +public sealed class InterfaceDescriptionTests { - public sealed class InterfaceDescriptionTests + [Fact] + public void InterfaceDescription_CreateInterfaceDescription() { - [Fact] - public void InterfaceDescription_CreateInterfaceDescription() - { - // Arrange - Type type = typeof(ITestActor); + // Arrange + Type type = typeof(ITestActor); - // Act - TestDescription description = new(type); + // Act + TestDescription description = new(type); - // Assert - description.ShouldNotBeNull(); + // Assert + description.ShouldNotBeNull(); - description.InterfaceType.ShouldBe(type); - description.Id.ShouldNotBe(0); - description.V1Id.ShouldBe(0); - description.Methods.ShouldBeEmpty(); - } + description.InterfaceType.ShouldBe(type); + description.Id.ShouldNotBe(0); + description.V1Id.ShouldBe(0); + description.Methods.ShouldBeEmpty(); + } - [Fact] - public void InterfaceDescription_CreateCrcIdAndV1Id_WhenUseCrcIdGenerationIsSet() - { - // Arrange - Type type = typeof(ITestActor); + [Fact] + public void InterfaceDescription_CreateCrcIdAndV1Id_WhenUseCrcIdGenerationIsSet() + { + // Arrange + Type type = typeof(ITestActor); - // Act - TestDescription description = new(type, useCRCIdGeneration: true); + // Act + TestDescription description = new(type, useCRCIdGeneration: true); - // Assert - description.Id.ShouldBe(-934188464); - description.V1Id.ShouldNotBe(0); - } + // Assert + description.Id.ShouldBe(-934188464); + description.V1Id.ShouldNotBe(0); + } - [Fact] - public void InterfaceDescription_CreateThrowsArgumentException_WhenTypeIsGenericDefinition() - { - // Arrange - Type type = typeof(IGenericActor<>); + [Fact] + public void InterfaceDescription_CreateThrowsArgumentException_WhenTypeIsGenericDefinition() + { + // Arrange + Type type = typeof(IGenericActor<>); - // Act - Action action = () => { TestDescription _ = new(type); }; + // Act + Action action = () => { TestDescription _ = new(type); }; - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } - [Fact] - public void InterfaceDescription_CreateThrowsArgumentException_WhenTypeIsGeneric() - { - // Arrange - Type type = typeof(IGenericActor); + [Fact] + public void InterfaceDescription_CreateThrowsArgumentException_WhenTypeIsGeneric() + { + // Arrange + Type type = typeof(IGenericActor); - // Act - Action action = () => { TestDescription _ = new(type); }; + // Act + Action action = () => { TestDescription _ = new(type); }; - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1\[.*IActor.*\]' is using generics. Generic interfaces cannot be remoted.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1\[.*IActor.*\]' is using generics. Generic interfaces cannot be remoted.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } - [Fact] - public void InterfaceDescription_CreateGeneratesMethodDescriptions_WhenTypeHasTaskMethods_ButDoesNotSeeInheritedMethods() - { - // Arrange - Type type = typeof(IChildActor); + [Fact] + public void InterfaceDescription_CreateGeneratesMethodDescriptions_WhenTypeHasTaskMethods_ButDoesNotSeeInheritedMethods() + { + // Arrange + Type type = typeof(IChildActor); - // Act - TestDescription description = new(type); + // Act + TestDescription description = new(type); - // Assert - description.Methods.ShouldNotBeNull(); - description.Methods.ShouldBeOfType(); - description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetInt"}}); - } + // Assert + description.Methods.ShouldNotBeNull(); + description.Methods.ShouldBeOfType(); + description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetInt"}}); + } - [Fact] - public void InterfaceDescription_CreateGeneratesMethodDescriptions_WhenTypeHasVoidMethods() - { - // Arrange - Type type = typeof(IVoidActor); + [Fact] + public void InterfaceDescription_CreateGeneratesMethodDescriptions_WhenTypeHasVoidMethods() + { + // Arrange + Type type = typeof(IVoidActor); - // Act - TestDescription description = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); + // Act + TestDescription description = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); - // Assert - description.Methods.ShouldNotBeNull(); - description.Methods.ShouldBeOfType(); - description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetString"}, new {Name="MethodWithArguments"}}); - } + // Assert + description.Methods.ShouldNotBeNull(); + description.Methods.ShouldBeOfType(); + description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetString"}, new {Name="MethodWithArguments"}}); + } - [Fact] - public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodsAreNotReturningTask() - { - // Arrange - Type type = typeof(IVoidActor); - - // Act - Action action = () => - { - TestDescription _ = new(type); - }; - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IVoidActor' does not return Task or Task<>. The actor interface methods must be async and must return either Task or Task<>.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } + [Fact] + public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodsAreNotReturningTask() + { + // Arrange + Type type = typeof(IVoidActor); - [Fact] - public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodsAreNotReturningVoid() + // Act + Action action = () => { - // Arrange - Type type = typeof(IMethodActor); + TestDescription _ = new(type); + }; - // Act - Action action = () => { TestDescription _ = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); }; + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IVoidActor' does not return Task or Task<>. The actor interface methods must be async and must return either Task or Task<>.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IMethodActor' returns '.*\.Task`1\[.*System.String.*\]'.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } + [Fact] + public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodsAreNotReturningVoid() + { + // Arrange + Type type = typeof(IMethodActor); - [Fact] - public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodsAreOverloaded() - { - // Arrange - Type type = typeof(IOverloadedMethodActor); + // Act + Action action = () => { TestDescription _ = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); }; - // Act - Action action = () => { TestDescription _ = new(type); }; + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IMethodActor' returns '.*\.Task`1\[.*System.String.*\]'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } + [Fact] + public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodsAreOverloaded() + { + // Arrange + Type type = typeof(IOverloadedMethodActor); - [Fact] - public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodIsGeneric() - { - // Arrange - Type type = typeof(IGenericMethodActor); + // Act + Action action = () => { TestDescription _ = new(type); }; - // Act - Action action = () => { TestDescription _ = new(type); }; + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'Get' of actor interface '.*\+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } + [Fact] + public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodIsGeneric() + { + // Arrange + Type type = typeof(IGenericMethodActor); - [Fact] - public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodHasVariableArguments() - { - // Arrange - Type type = typeof(IVariableActor); + // Act + Action action = () => { TestDescription _ = new(type); }; - // Act - Action action = () => { TestDescription _ = new(type); }; + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'Get' of actor interface '.*\+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'MethodWithVarArgs' of actor interface '.*\+IVariableActor' is using a variable argument list. The actor interface methods cannot have a variable argument list.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } + [Fact] + public void InterfaceDescription_CreateThrowsArgumentException_WhenMethodHasVariableArguments() + { + // Arrange + Type type = typeof(IVariableActor); - internal interface ITestActor : IActor - { - } + // Act + Action action = () => { TestDescription _ = new(type); }; - internal interface IGenericActor : IActor - { - } + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithVarArgs' of actor interface '.*\+IVariableActor' is using a variable argument list. The actor interface methods cannot have a variable argument list.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } - internal interface IMethodActor : IActor - { - Task GetString(); + internal interface ITestActor : IActor + { + } - Task MethodWithArguments(int number, bool choice, string information); - } + internal interface IGenericActor : IActor + { + } - internal interface IChildActor : IMethodActor - { - Task GetInt(); - } + internal interface IMethodActor : IActor + { + Task GetString(); - internal interface IVariableActor : IActor - { - Task MethodWithVarArgs(__arglist); - } + Task MethodWithArguments(int number, bool choice, string information); + } - internal interface IVoidActor : IActor - { - void GetString(); + internal interface IChildActor : IMethodActor + { + Task GetInt(); + } - void MethodWithArguments(int number, bool choice, string information); - } + internal interface IVariableActor : IActor + { + Task MethodWithVarArgs(__arglist); + } - internal interface IOverloadedActor : IMethodActor - { - Task GetString(string parameter); - } + internal interface IVoidActor : IActor + { + void GetString(); - internal interface IOverloadedMethodActor : IActor - { - Task GetString(); + void MethodWithArguments(int number, bool choice, string information); + } - Task GetString(string parameter); - } + internal interface IOverloadedActor : IMethodActor + { + Task GetString(string parameter); + } - internal interface IGenericMethodActor : IActor - { - Task Get(); - } + internal interface IOverloadedMethodActor : IActor + { + Task GetString(); + + Task GetString(string parameter); + } - internal class TestDescription : InterfaceDescription + internal interface IGenericMethodActor : IActor + { + Task Get(); + } + + internal class TestDescription : InterfaceDescription + { + public TestDescription( + Type remotedInterfaceType, + string remotedInterfaceKindName = "actor", + bool useCRCIdGeneration = false, + MethodReturnCheck methodReturnCheck = MethodReturnCheck.EnsureReturnsTask) + : base(remotedInterfaceKindName, remotedInterfaceType, useCRCIdGeneration, methodReturnCheck) { - public TestDescription( - Type remotedInterfaceType, - string remotedInterfaceKindName = "actor", - bool useCRCIdGeneration = false, - MethodReturnCheck methodReturnCheck = MethodReturnCheck.EnsureReturnsTask) - : base(remotedInterfaceKindName, remotedInterfaceType, useCRCIdGeneration, methodReturnCheck) - { - } } } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs b/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs index 0841298a9..ec390a94c 100644 --- a/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs @@ -17,106 +17,105 @@ using Shouldly; using Xunit; -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +public sealed class MethodArgumentDescriptionTests { - public sealed class MethodArgumentDescriptionTests + [Fact] + public void MethodArgumentDescription_CreatMethodArgumentDescription() + { + // Arrange + MethodInfo methodInfo = typeof(ITestActor).GetMethod("MethodWithArguments"); + ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; + + // Act + var description = MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); + + // Assert + description.ShouldNotBeNull(); + + description.Name.ShouldBe("number"); + description.ArgumentType.ShouldBe(typeof(int)); + } + + [Fact] + public void MethodDescription_CreateThrowsArgumentException_WhenParameterHasVariableLength() + { + // Arrange + Type type = typeof(ITestActor); + MethodInfo methodInfo = type.GetMethod("MethodWithParams"); + ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; + + // Act + Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithParams' of actor interface '.*\+ITestActor' has variable length parameter 'values'. The actor interface methods must not have variable length parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void MethodDescription_CreateThrowsArgumentException_WhenParameterIsInput() { - [Fact] - public void MethodArgumentDescription_CreatMethodArgumentDescription() - { - // Arrange - MethodInfo methodInfo = typeof(ITestActor).GetMethod("MethodWithArguments"); - ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; - - // Act - var description = MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); - - // Assert - description.ShouldNotBeNull(); - - description.Name.ShouldBe("number"); - description.ArgumentType.ShouldBe(typeof(int)); - } - - [Fact] - public void MethodDescription_CreateThrowsArgumentException_WhenParameterHasVariableLength() - { - // Arrange - Type type = typeof(ITestActor); - MethodInfo methodInfo = type.GetMethod("MethodWithParams"); - ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; - - // Act - Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'MethodWithParams' of actor interface '.*\+ITestActor' has variable length parameter 'values'. The actor interface methods must not have variable length parameters.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void MethodDescription_CreateThrowsArgumentException_WhenParameterIsInput() - { - // Arrange - Type type = typeof(ITestActor); - MethodInfo methodInfo = type.GetMethod("MethodWithIn"); - ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; - - // Act - Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'MethodWithIn' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void MethodDescription_CreateThrowsArgumentException_WhenParameterIsOutput() - { - // Arrange - Type type = typeof(ITestActor); - MethodInfo methodInfo = type.GetMethod("MethodWithOut"); - ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; - - // Act - Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'MethodWithOut' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void MethodDescription_CreateThrowsArgumentException_WhenParameterIsOptional() - { - // Arrange - Type type = typeof(ITestActor); - MethodInfo methodInfo = type.GetMethod("MethodWithOptional"); - ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; - - // Act - Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'MethodWithOptional' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - internal interface ITestActor : IActor - { - Task MethodWithArguments(int number, bool choice, string information); - - Task MethodWithParams(params string[] values); - - Task MethodWithOut(out int value); - - Task MethodWithIn(in int value); - - Task MethodWithOptional(int value = 1); - } + // Arrange + Type type = typeof(ITestActor); + MethodInfo methodInfo = type.GetMethod("MethodWithIn"); + ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; + + // Act + Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithIn' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void MethodDescription_CreateThrowsArgumentException_WhenParameterIsOutput() + { + // Arrange + Type type = typeof(ITestActor); + MethodInfo methodInfo = type.GetMethod("MethodWithOut"); + ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; + + // Act + Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithOut' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void MethodDescription_CreateThrowsArgumentException_WhenParameterIsOptional() + { + // Arrange + Type type = typeof(ITestActor); + MethodInfo methodInfo = type.GetMethod("MethodWithOptional"); + ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; + + // Act + Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithOptional' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + internal interface ITestActor : IActor + { + Task MethodWithArguments(int number, bool choice, string information); + + Task MethodWithParams(params string[] values); + + Task MethodWithOut(out int value); + + Task MethodWithIn(in int value); + + Task MethodWithOptional(int value = 1); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs b/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs index c8162f5f0..a90a01bb5 100644 --- a/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs @@ -19,114 +19,113 @@ using Shouldly; using Xunit; -namespace Dapr.Actors.Description +namespace Dapr.Actors.Description; + +public sealed class MethodDescriptionTests { - public sealed class MethodDescriptionTests + [Fact] + public void MethodDescription_CreatMethodDescription() + { + // Arrange + MethodInfo methodInfo = typeof(ITestActor).GetMethod("GetString"); + + // Act + var description = MethodDescription.Create("actor", methodInfo, false); + + // Assert + description.ShouldNotBeNull(); + + description.MethodInfo.ShouldBeSameAs(methodInfo); + description.Name.ShouldBe("GetString"); + description.ReturnType.ShouldBe(typeof(Task)); + description.Id.ShouldNotBe(0); + description.Arguments.ShouldBeEmpty(); + description.HasCancellationToken.ShouldBeFalse(); + } + + [Fact] + public void MethodDescription_CreateCrcId_WhenUseCrcIdGenerationIsSet() + { + // Arrange + MethodInfo methodInfo = typeof(ITestActor).GetMethod("GetString"); + + // Act + var description = MethodDescription.Create("actor", methodInfo, true); + + // Assert + description.Id.ShouldBe(70257263); + } + + [Fact] + public void MethodDescription_CreateGeneratesArgumentDescriptions_WhenMethodHasArguments() + { + // Arrange + MethodInfo methodInfo = typeof(ITestActor).GetMethod("MethodWithArguments"); + + // Act + var description = MethodDescription.Create("actor", methodInfo, false); + + // Assert + description.Arguments.ShouldNotBeNull(); + description.Arguments.ShouldBeOfType(); + description.Arguments.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "number"}, new {Name = "choice"}, new {Name = "information"}}); + } + + [Fact] + public void MethodDescription_CreateSetsHasCancellationTokenToTrue_WhenMethodHasTokenAsArgument() + { + // Arrange + MethodInfo methodInfo = typeof(ITestActor).GetMethod("MethodWithToken"); + + // Act + var description = MethodDescription.Create("actor", methodInfo, false); + + // Assert + description.HasCancellationToken.ShouldBeTrue(); + } + + [Fact] + public void MethodDescription_CreateThrowsArgumentException_WhenMethodHasTokenAsNonLastArgument() + { + // Arrange + Type type = typeof(ITestActor); + MethodInfo methodInfo = type.GetMethod("MethodWithTokenNotLast"); + + // Act + Action action = () => MethodDescription.Create("actor", methodInfo, false); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithTokenNotLast' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter\..*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + [Fact] + public void MethodDescription_CreateThrowsArgumentException_WhenMethodHasMultipleCancellationTokens() { - [Fact] - public void MethodDescription_CreatMethodDescription() - { - // Arrange - MethodInfo methodInfo = typeof(ITestActor).GetMethod("GetString"); - - // Act - var description = MethodDescription.Create("actor", methodInfo, false); - - // Assert - description.ShouldNotBeNull(); - - description.MethodInfo.ShouldBeSameAs(methodInfo); - description.Name.ShouldBe("GetString"); - description.ReturnType.ShouldBe(typeof(Task)); - description.Id.ShouldNotBe(0); - description.Arguments.ShouldBeEmpty(); - description.HasCancellationToken.ShouldBeFalse(); - } - - [Fact] - public void MethodDescription_CreateCrcId_WhenUseCrcIdGenerationIsSet() - { - // Arrange - MethodInfo methodInfo = typeof(ITestActor).GetMethod("GetString"); - - // Act - var description = MethodDescription.Create("actor", methodInfo, true); - - // Assert - description.Id.ShouldBe(70257263); - } - - [Fact] - public void MethodDescription_CreateGeneratesArgumentDescriptions_WhenMethodHasArguments() - { - // Arrange - MethodInfo methodInfo = typeof(ITestActor).GetMethod("MethodWithArguments"); - - // Act - var description = MethodDescription.Create("actor", methodInfo, false); - - // Assert - description.Arguments.ShouldNotBeNull(); - description.Arguments.ShouldBeOfType(); - description.Arguments.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "number"}, new {Name = "choice"}, new {Name = "information"}}); - } - - [Fact] - public void MethodDescription_CreateSetsHasCancellationTokenToTrue_WhenMethodHasTokenAsArgument() - { - // Arrange - MethodInfo methodInfo = typeof(ITestActor).GetMethod("MethodWithToken"); - - // Act - var description = MethodDescription.Create("actor", methodInfo, false); - - // Assert - description.HasCancellationToken.ShouldBeTrue(); - } - - [Fact] - public void MethodDescription_CreateThrowsArgumentException_WhenMethodHasTokenAsNonLastArgument() - { - // Arrange - Type type = typeof(ITestActor); - MethodInfo methodInfo = type.GetMethod("MethodWithTokenNotLast"); - - // Act - Action action = () => MethodDescription.Create("actor", methodInfo, false); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'MethodWithTokenNotLast' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter\..*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - [Fact] - public void MethodDescription_CreateThrowsArgumentException_WhenMethodHasMultipleCancellationTokens() - { - // Arrange - Type type = typeof(ITestActor); - MethodInfo methodInfo = type.GetMethod("MethodWithMultipleTokens"); - - // Act - Action action = () => MethodDescription.Create("actor", methodInfo, false); - - // Assert - var exception = Should.Throw(action); - exception.Message.ShouldMatch(@"Method 'MethodWithMultipleTokens' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter.*"); - exception.ParamName.ShouldBe("actorInterfaceType"); - } - - internal interface ITestActor : IActor - { - Task GetString(); - - Task MethodWithArguments(int number, bool choice, string information); - - Task MethodWithToken(CancellationToken cancellationToken); - - Task MethodWithMultipleTokens(CancellationToken cancellationToken, CancellationToken cancellationTokenToo); - - Task MethodWithTokenNotLast(CancellationToken cancellationToken, bool additionalArgument); - } + // Arrange + Type type = typeof(ITestActor); + MethodInfo methodInfo = type.GetMethod("MethodWithMultipleTokens"); + + // Act + Action action = () => MethodDescription.Create("actor", methodInfo, false); + + // Assert + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithMultipleTokens' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); + } + + internal interface ITestActor : IActor + { + Task GetString(); + + Task MethodWithArguments(int number, bool choice, string information); + + Task MethodWithToken(CancellationToken cancellationToken); + + Task MethodWithMultipleTokens(CancellationToken cancellationToken, CancellationToken cancellationTokenToo); + + Task MethodWithTokenNotLast(CancellationToken cancellationToken, bool additionalArgument); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs b/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs index b27e9afe3..0054a73b9 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs @@ -18,242 +18,241 @@ using Microsoft.Extensions.Logging.Abstractions; using Xunit; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +public sealed class ActorManagerTests { - public sealed class ActorManagerTests + private ActorManager CreateActorManager(Type type, ActorActivator activator = null) { - private ActorManager CreateActorManager(Type type, ActorActivator activator = null) - { - var registration = new ActorRegistration(ActorTypeInformation.Get(type, actorTypeName: null)); - var interactor = new DaprHttpInteractor(clientHandler: null, "http://localhost:3500", apiToken: null, requestTimeout: null); - return new ActorManager(registration, activator ?? new DefaultActorActivator(), JsonSerializerDefaults.Web, false, NullLoggerFactory.Instance, ActorProxy.DefaultProxyFactory, interactor); - } + var registration = new ActorRegistration(ActorTypeInformation.Get(type, actorTypeName: null)); + var interactor = new DaprHttpInteractor(clientHandler: null, "http://localhost:3500", apiToken: null, requestTimeout: null); + return new ActorManager(registration, activator ?? new DefaultActorActivator(), JsonSerializerDefaults.Web, false, NullLoggerFactory.Instance, ActorProxy.DefaultProxyFactory, interactor); + } - [Fact] - public async Task ActivateActorAsync_CreatesActorAndCallsActivateLifecycleMethod() - { - var manager = CreateActorManager(typeof(TestActor)); + [Fact] + public async Task ActivateActorAsync_CreatesActorAndCallsActivateLifecycleMethod() + { + var manager = CreateActorManager(typeof(TestActor)); - var id = ActorId.CreateRandom(); - await manager.ActivateActorAsync(id); + var id = ActorId.CreateRandom(); + await manager.ActivateActorAsync(id); - Assert.True(manager.TryGetActorAsync(id, out var actor)); - Assert.True(Assert.IsType(actor).IsActivated); - } + Assert.True(manager.TryGetActorAsync(id, out var actor)); + Assert.True(Assert.IsType(actor).IsActivated); + } - [Fact] - public async Task ActivateActorAsync_CanActivateMultipleActors() - { - var manager = CreateActorManager(typeof(TestActor)); + [Fact] + public async Task ActivateActorAsync_CanActivateMultipleActors() + { + var manager = CreateActorManager(typeof(TestActor)); - await manager.ActivateActorAsync(new ActorId("1")); - Assert.True(manager.TryGetActorAsync(new ActorId("1"), out var actor1)); + await manager.ActivateActorAsync(new ActorId("1")); + Assert.True(manager.TryGetActorAsync(new ActorId("1"), out var actor1)); - await manager.ActivateActorAsync(new ActorId("2")); - Assert.True(manager.TryGetActorAsync(new ActorId("2"), out var actor2)); + await manager.ActivateActorAsync(new ActorId("2")); + Assert.True(manager.TryGetActorAsync(new ActorId("2"), out var actor2)); - Assert.NotSame(actor1, actor2); - } + Assert.NotSame(actor1, actor2); + } - [Fact] - public async Task ActivateActorAsync_UsesActivator() - { - var activator = new TestActivator(); + [Fact] + public async Task ActivateActorAsync_UsesActivator() + { + var activator = new TestActivator(); - var manager = CreateActorManager(typeof(TestActor), activator); + var manager = CreateActorManager(typeof(TestActor), activator); - var id = ActorId.CreateRandom(); - await manager.ActivateActorAsync(id); + var id = ActorId.CreateRandom(); + await manager.ActivateActorAsync(id); - Assert.Equal(1, activator.CreateCallCount); - } + Assert.Equal(1, activator.CreateCallCount); + } - [Fact] - public async Task ActivateActorAsync_DoubleActivation_DeactivatesNewActor() - { - // We have to use the activator to observe the behavior here. We don't - // have a way to interact with the "new" actor that gets destroyed immediately. - var activator = new TestActivator(); + [Fact] + public async Task ActivateActorAsync_DoubleActivation_DeactivatesNewActor() + { + // We have to use the activator to observe the behavior here. We don't + // have a way to interact with the "new" actor that gets destroyed immediately. + var activator = new TestActivator(); - var manager = CreateActorManager(typeof(TestActor), activator); + var manager = CreateActorManager(typeof(TestActor), activator); - var id = ActorId.CreateRandom(); - await manager.ActivateActorAsync(id); + var id = ActorId.CreateRandom(); + await manager.ActivateActorAsync(id); - Assert.True(manager.TryGetActorAsync(id, out var original)); + Assert.True(manager.TryGetActorAsync(id, out var original)); - // It's a double-activation! We don't expect the runtime to do this, but the code - // handles it. - await manager.ActivateActorAsync(id); + // It's a double-activation! We don't expect the runtime to do this, but the code + // handles it. + await manager.ActivateActorAsync(id); - // Still holding the original actor - Assert.True(manager.TryGetActorAsync(id, out var another)); - Assert.Same(original, another); - Assert.False(Assert.IsType(another).IsDeactivated); - Assert.False(Assert.IsType(another).IsDisposed); + // Still holding the original actor + Assert.True(manager.TryGetActorAsync(id, out var another)); + Assert.Same(original, another); + Assert.False(Assert.IsType(another).IsDeactivated); + Assert.False(Assert.IsType(another).IsDisposed); - // We should have seen 2 create operations and 1 delete - Assert.Equal(2, activator.CreateCallCount); - Assert.Equal(1, activator.DeleteCallCount); - } + // We should have seen 2 create operations and 1 delete + Assert.Equal(2, activator.CreateCallCount); + Assert.Equal(1, activator.DeleteCallCount); + } - [Fact] - public async Task ActivateActorAsync_ExceptionDuringActivation_ActorNotStoredAndDeleted() - { - var activator = new TestActivator(); + [Fact] + public async Task ActivateActorAsync_ExceptionDuringActivation_ActorNotStoredAndDeleted() + { + var activator = new TestActivator(); - var manager = CreateActorManager(typeof(ThrowsDuringOnActivateAsync), activator); + var manager = CreateActorManager(typeof(ThrowsDuringOnActivateAsync), activator); - var id = ActorId.CreateRandom(); + var id = ActorId.CreateRandom(); - await Assert.ThrowsAsync(async () => - { - await manager.ActivateActorAsync(id); - }); + await Assert.ThrowsAsync(async () => + { + await manager.ActivateActorAsync(id); + }); - Assert.False(manager.TryGetActorAsync(id, out _)); - Assert.Equal(1, activator.DeleteCallCount); - } + Assert.False(manager.TryGetActorAsync(id, out _)); + Assert.Equal(1, activator.DeleteCallCount); + } - [Fact] - public async Task DectivateActorAsync_DeletesActorAndCallsDeactivateLifecycleMethod() - { - var manager = CreateActorManager(typeof(TestActor)); + [Fact] + public async Task DectivateActorAsync_DeletesActorAndCallsDeactivateLifecycleMethod() + { + var manager = CreateActorManager(typeof(TestActor)); - var id = ActorId.CreateRandom(); - await manager.ActivateActorAsync(id); + var id = ActorId.CreateRandom(); + await manager.ActivateActorAsync(id); - Assert.True(manager.TryGetActorAsync(id, out var actor)); - await manager.DeactivateActorAsync(id); + Assert.True(manager.TryGetActorAsync(id, out var actor)); + await manager.DeactivateActorAsync(id); - Assert.True(Assert.IsType(actor).IsDeactivated); - Assert.True(Assert.IsType(actor).IsDisposed); - } + Assert.True(Assert.IsType(actor).IsDeactivated); + Assert.True(Assert.IsType(actor).IsDisposed); + } - [Fact] - public async Task DeactivateActorAsync_ItsOkToDeactivateNonExistentActor() - { - var manager = CreateActorManager(typeof(TestActor)); + [Fact] + public async Task DeactivateActorAsync_ItsOkToDeactivateNonExistentActor() + { + var manager = CreateActorManager(typeof(TestActor)); - var id = ActorId.CreateRandom(); - Assert.False(manager.TryGetActorAsync(id, out _)); - await manager.DeactivateActorAsync(id); - } + var id = ActorId.CreateRandom(); + Assert.False(manager.TryGetActorAsync(id, out _)); + await manager.DeactivateActorAsync(id); + } - [Fact] - public async Task DeactivateActorAsync_UsesActivator() - { - var activator = new TestActivator(); + [Fact] + public async Task DeactivateActorAsync_UsesActivator() + { + var activator = new TestActivator(); - var manager = CreateActorManager(typeof(TestActor), activator); + var manager = CreateActorManager(typeof(TestActor), activator); - var id = ActorId.CreateRandom(); - await manager.ActivateActorAsync(id); - await manager.DeactivateActorAsync(id); + var id = ActorId.CreateRandom(); + await manager.ActivateActorAsync(id); + await manager.DeactivateActorAsync(id); - Assert.Equal(1, activator.CreateCallCount); - Assert.Equal(1, activator.DeleteCallCount); - } + Assert.Equal(1, activator.CreateCallCount); + Assert.Equal(1, activator.DeleteCallCount); + } - [Fact] - public async Task DeactivateActorAsync_ExceptionDuringDeactivation_ActorIsRemovedAndDeleted() - { - var activator = new TestActivator(); + [Fact] + public async Task DeactivateActorAsync_ExceptionDuringDeactivation_ActorIsRemovedAndDeleted() + { + var activator = new TestActivator(); - var manager = CreateActorManager(typeof(ThrowsDuringOnDeactivateAsync), activator); + var manager = CreateActorManager(typeof(ThrowsDuringOnDeactivateAsync), activator); - var id = ActorId.CreateRandom(); - await manager.ActivateActorAsync(id); - Assert.True(manager.TryGetActorAsync(id, out _)); + var id = ActorId.CreateRandom(); + await manager.ActivateActorAsync(id); + Assert.True(manager.TryGetActorAsync(id, out _)); - await Assert.ThrowsAsync(async () => - { - await manager.DeactivateActorAsync(id); - }); + await Assert.ThrowsAsync(async () => + { + await manager.DeactivateActorAsync(id); + }); - Assert.False(manager.TryGetActorAsync(id, out _)); - Assert.Equal(1, activator.DeleteCallCount); - } + Assert.False(manager.TryGetActorAsync(id, out _)); + Assert.Equal(1, activator.DeleteCallCount); + } + + private interface ITestActor : IActor { } - private interface ITestActor : IActor { } + private class TestActor : Actor, ITestActor, IDisposable + { + private static int counter; - private class TestActor : Actor, ITestActor, IDisposable + public TestActor(ActorHost host) : base(host) { - private static int counter; + Sequence = Interlocked.Increment(ref counter); + } - public TestActor(ActorHost host) : base(host) - { - Sequence = Interlocked.Increment(ref counter); - } + // Makes instances easier to tell apart for debugging. + public int Sequence { get; } - // Makes instances easier to tell apart for debugging. - public int Sequence { get; } + public bool IsActivated { get; set; } - public bool IsActivated { get; set; } + public bool IsDeactivated { get; set; } - public bool IsDeactivated { get; set; } + public bool IsDisposed { get; set; } - public bool IsDisposed { get; set; } + public void Dispose() + { + IsDisposed = true; + } - public void Dispose() - { - IsDisposed = true; - } + protected override Task OnActivateAsync() + { + IsActivated = true; + return Task.CompletedTask; + } - protected override Task OnActivateAsync() - { - IsActivated = true; - return Task.CompletedTask; - } + protected override Task OnDeactivateAsync() + { + IsDeactivated = true; + return Task.CompletedTask; + } + } - protected override Task OnDeactivateAsync() - { - IsDeactivated = true; - return Task.CompletedTask; - } + private class ThrowsDuringOnActivateAsync : Actor, ITestActor + { + public ThrowsDuringOnActivateAsync(ActorHost host) : base(host) + { } - private class ThrowsDuringOnActivateAsync : Actor, ITestActor + protected override Task OnActivateAsync() { - public ThrowsDuringOnActivateAsync(ActorHost host) : base(host) - { - } - - protected override Task OnActivateAsync() - { - throw new InvalidTimeZoneException(); - } + throw new InvalidTimeZoneException(); } + } - private class ThrowsDuringOnDeactivateAsync : Actor, ITestActor + private class ThrowsDuringOnDeactivateAsync : Actor, ITestActor + { + public ThrowsDuringOnDeactivateAsync(ActorHost host) : base(host) { - public ThrowsDuringOnDeactivateAsync(ActorHost host) : base(host) - { - } - - protected override Task OnDeactivateAsync() - { - throw new InvalidTimeZoneException(); - } } - private class TestActivator : DefaultActorActivator + protected override Task OnDeactivateAsync() { - public int CreateCallCount { get; set; } + throw new InvalidTimeZoneException(); + } + } - public int DeleteCallCount { get; set; } + private class TestActivator : DefaultActorActivator + { + public int CreateCallCount { get; set; } - public override Task CreateAsync(ActorHost host) - { - CreateCallCount++;; - return base.CreateAsync(host); - } + public int DeleteCallCount { get; set; } - public override Task DeleteAsync(ActorActivatorState state) - { - DeleteCallCount++; - return base.DeleteAsync(state); - } + public override Task CreateAsync(ActorHost host) + { + CreateCallCount++;; + return base.CreateAsync(host); + } + + public override Task DeleteAsync(ActorActivatorState state) + { + DeleteCallCount++; + return base.DeleteAsync(state); } } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Runtime/ActorReminderInfoTests.cs b/test/Dapr.Actors.Test/Runtime/ActorReminderInfoTests.cs index 9ff0ad2b1..971a7c8a8 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorReminderInfoTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorReminderInfoTests.cs @@ -4,31 +4,30 @@ using System.Threading.Tasks; using Xunit; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +public class ActorReminderInfoTests { - public class ActorReminderInfoTests + [Fact] + public async Task TestActorReminderInfo_SerializeExcludesNullTtl() { - [Fact] - public async Task TestActorReminderInfo_SerializeExcludesNullTtl() - { - var info = new ReminderInfo(new byte[] { }, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), 1); - var serialized = await info.SerializeAsync(); + var info = new ReminderInfo(new byte[] { }, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), 1); + var serialized = await info.SerializeAsync(); - Assert.DoesNotContain("ttl", serialized); - var info2 = await ReminderInfo.DeserializeAsync(new MemoryStream(Encoding.UTF8.GetBytes(serialized))); - Assert.Null(info2.Ttl); - } + Assert.DoesNotContain("ttl", serialized); + var info2 = await ReminderInfo.DeserializeAsync(new MemoryStream(Encoding.UTF8.GetBytes(serialized))); + Assert.Null(info2.Ttl); + } - [Fact] - public async Task TestActorReminderInfo_SerializeIncludesTtlWhenSet() - { - var info = new ReminderInfo(new byte[] { }, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), 1, TimeSpan.FromSeconds(1)); - var serialized = await info.SerializeAsync(); + [Fact] + public async Task TestActorReminderInfo_SerializeIncludesTtlWhenSet() + { + var info = new ReminderInfo(new byte[] { }, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), 1, TimeSpan.FromSeconds(1)); + var serialized = await info.SerializeAsync(); - Assert.Contains("ttl", serialized); - var info2 = await ReminderInfo.DeserializeAsync(new MemoryStream(Encoding.UTF8.GetBytes(serialized))); - Assert.NotNull(info2.Ttl); - Assert.Equal(TimeSpan.FromSeconds(1), info2.Ttl); - } + Assert.Contains("ttl", serialized); + var info2 = await ReminderInfo.DeserializeAsync(new MemoryStream(Encoding.UTF8.GetBytes(serialized))); + Assert.NotNull(info2.Ttl); + Assert.Equal(TimeSpan.FromSeconds(1), info2.Ttl); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs index 6701535fc..9d7c0c697 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs @@ -11,141 +11,140 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test.Runtime -{ - using Dapr.Actors.Runtime; - using Moq; - using Xunit; - using System; - using Shouldly; +namespace Dapr.Actors.Test.Runtime; + +using Dapr.Actors.Runtime; +using Moq; +using Xunit; +using System; +using Shouldly; - public sealed class ActorRuntimeOptionsTests +public sealed class ActorRuntimeOptionsTests +{ + [Fact] + public void TestRegisterActor_SavesActivator() { - [Fact] - public void TestRegisterActor_SavesActivator() - { - var actorType = typeof(TestActor); - var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); + var actorType = typeof(TestActor); + var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); + + var activator = Mock.Of(); - var activator = Mock.Of(); + var actorRuntimeOptions = new ActorRuntimeOptions(); + actorRuntimeOptions.Actors.RegisterActor(registration => + { + registration.Activator = activator; + }); - var actorRuntimeOptions = new ActorRuntimeOptions(); - actorRuntimeOptions.Actors.RegisterActor(registration => + Assert.Collection( + actorRuntimeOptions.Actors, + registration => { - registration.Activator = activator; + Assert.Same(actorTypeInformation.ImplementationType, registration.Type.ImplementationType); + Assert.Same(activator, registration.Activator); }); + } - Assert.Collection( - actorRuntimeOptions.Actors, - registration => - { - Assert.Same(actorTypeInformation.ImplementationType, registration.Type.ImplementationType); - Assert.Same(activator, registration.Activator); - }); - } - - [Fact] - public void SettingActorIdleTimeout_Succeeds() - { - var options = new ActorRuntimeOptions(); - options.ActorIdleTimeout = TimeSpan.FromSeconds(1); + [Fact] + public void SettingActorIdleTimeout_Succeeds() + { + var options = new ActorRuntimeOptions(); + options.ActorIdleTimeout = TimeSpan.FromSeconds(1); - Assert.Equal(TimeSpan.FromSeconds(1), options.ActorIdleTimeout); - } + Assert.Equal(TimeSpan.FromSeconds(1), options.ActorIdleTimeout); + } - [Fact] - public void SettingActorIdleTimeoutToLessThanZero_Fails() - { - var options = new ActorRuntimeOptions(); - Action action = () => options.ActorIdleTimeout = TimeSpan.FromSeconds(-1); + [Fact] + public void SettingActorIdleTimeoutToLessThanZero_Fails() + { + var options = new ActorRuntimeOptions(); + Action action = () => options.ActorIdleTimeout = TimeSpan.FromSeconds(-1); - action.ShouldThrow(); - } + action.ShouldThrow(); + } - [Fact] - public void SettingActorScanInterval_Succeeds() - { - var options = new ActorRuntimeOptions(); - options.ActorScanInterval = TimeSpan.FromSeconds(1); + [Fact] + public void SettingActorScanInterval_Succeeds() + { + var options = new ActorRuntimeOptions(); + options.ActorScanInterval = TimeSpan.FromSeconds(1); - Assert.Equal(TimeSpan.FromSeconds(1), options.ActorScanInterval); - } + Assert.Equal(TimeSpan.FromSeconds(1), options.ActorScanInterval); + } - [Fact] - public void SettingActorScanIntervalToLessThanZero_Fails() - { - var options = new ActorRuntimeOptions(); - Action action = () => options.ActorScanInterval = TimeSpan.FromSeconds(-1); + [Fact] + public void SettingActorScanIntervalToLessThanZero_Fails() + { + var options = new ActorRuntimeOptions(); + Action action = () => options.ActorScanInterval = TimeSpan.FromSeconds(-1); - action.ShouldThrow(); - } + action.ShouldThrow(); + } - [Fact] - public void SettingDrainOngoingCallTimeout_Succeeds() - { - var options = new ActorRuntimeOptions(); - options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(1); + [Fact] + public void SettingDrainOngoingCallTimeout_Succeeds() + { + var options = new ActorRuntimeOptions(); + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(1); - Assert.Equal(TimeSpan.FromSeconds(1), options.DrainOngoingCallTimeout); - } + Assert.Equal(TimeSpan.FromSeconds(1), options.DrainOngoingCallTimeout); + } - [Fact] - public void SettingDrainOngoingCallTimeoutToLessThanZero_Fails() - { - var options = new ActorRuntimeOptions(); - Action action = () => options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(-1); + [Fact] + public void SettingDrainOngoingCallTimeoutToLessThanZero_Fails() + { + var options = new ActorRuntimeOptions(); + Action action = () => options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(-1); - action.ShouldThrow(); - } + action.ShouldThrow(); + } - [Fact] - public void SettingJsonSerializerOptions_Succeeds() - { - var serializerOptions = new System.Text.Json.JsonSerializerOptions(); - var options = new ActorRuntimeOptions(); - options.JsonSerializerOptions = serializerOptions; + [Fact] + public void SettingJsonSerializerOptions_Succeeds() + { + var serializerOptions = new System.Text.Json.JsonSerializerOptions(); + var options = new ActorRuntimeOptions(); + options.JsonSerializerOptions = serializerOptions; - Assert.Same(serializerOptions, options.JsonSerializerOptions); - } + Assert.Same(serializerOptions, options.JsonSerializerOptions); + } - [Fact] - public void SettingJsonSerializerOptionsToNull_Fails() - { - var options = new ActorRuntimeOptions(); - Action action = () => options.JsonSerializerOptions = null; + [Fact] + public void SettingJsonSerializerOptionsToNull_Fails() + { + var options = new ActorRuntimeOptions(); + Action action = () => options.JsonSerializerOptions = null; - action.ShouldThrow(); - } + action.ShouldThrow(); + } - [Fact] - public void SettingRemindersStoragePartitionsToLessThanZero_Fails() - { - var options = new ActorRuntimeOptions(); - Action action = () => options.RemindersStoragePartitions = -1; + [Fact] + public void SettingRemindersStoragePartitionsToLessThanZero_Fails() + { + var options = new ActorRuntimeOptions(); + Action action = () => options.RemindersStoragePartitions = -1; - action.ShouldThrow(); - } + action.ShouldThrow(); + } - [Fact] - public void SettingReentrancyConfigWithoutMaxStackDepth_Succeeds() - { - var options = new ActorRuntimeOptions(); - options.ReentrancyConfig.Enabled = true; + [Fact] + public void SettingReentrancyConfigWithoutMaxStackDepth_Succeeds() + { + var options = new ActorRuntimeOptions(); + options.ReentrancyConfig.Enabled = true; - Assert.True(options.ReentrancyConfig.Enabled); - Assert.Null(options.ReentrancyConfig.MaxStackDepth); - } + Assert.True(options.ReentrancyConfig.Enabled); + Assert.Null(options.ReentrancyConfig.MaxStackDepth); + } - [Fact] - public void SettingReentrancyConfigWithMaxStackDepth_Succeeds() - { - var options = new ActorRuntimeOptions(); - options.ReentrancyConfig.Enabled = true; - options.ReentrancyConfig.MaxStackDepth = 64; + [Fact] + public void SettingReentrancyConfigWithMaxStackDepth_Succeeds() + { + var options = new ActorRuntimeOptions(); + options.ReentrancyConfig.Enabled = true; + options.ReentrancyConfig.MaxStackDepth = 64; - Assert.True(options.ReentrancyConfig.Enabled); - Assert.Equal(64, options.ReentrancyConfig.MaxStackDepth); - } + Assert.True(options.ReentrancyConfig.Enabled); + Assert.Equal(64, options.ReentrancyConfig.MaxStackDepth); } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs index 6ebfe1bb2..c39e940ee 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs @@ -11,468 +11,467 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.Buffers; +using System.Linq; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.Actors.Runtime; +using Microsoft.Extensions.Logging; +using Xunit; +using Dapr.Actors.Client; +using System.Reflection; +using System.Threading; + +public sealed class ActorRuntimeTests { - using System; - using System.Buffers; - using System.Linq; - using System.IO; - using System.Text; - using System.Text.Json; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.Actors.Runtime; - using Microsoft.Extensions.Logging; - using Xunit; - using Dapr.Actors.Client; - using System.Reflection; - using System.Threading; - - public sealed class ActorRuntimeTests - { - private const string RenamedActorTypeName = "MyRenamedActor"; - private const string ParamActorTypeName = "AnotherRenamedActor"; - private readonly ILoggerFactory loggerFactory = new LoggerFactory(); - private readonly ActorActivatorFactory activatorFactory = new DefaultActorActivatorFactory(); + private const string RenamedActorTypeName = "MyRenamedActor"; + private const string ParamActorTypeName = "AnotherRenamedActor"; + private readonly ILoggerFactory loggerFactory = new LoggerFactory(); + private readonly ActorActivatorFactory activatorFactory = new DefaultActorActivatorFactory(); - private readonly IActorProxyFactory proxyFactory = ActorProxy.DefaultProxyFactory; + private readonly IActorProxyFactory proxyFactory = ActorProxy.DefaultProxyFactory; - private interface ITestActor : IActor - { - } + private interface ITestActor : IActor + { + } - [Fact] - public void TestInferredActorType() - { - var actorType = typeof(TestActor); + [Fact] + public void TestInferredActorType() + { + var actorType = typeof(TestActor); - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(); + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - } + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + } - [Fact] - public void TestExplicitActorType() - { - var actorType = typeof(RenamedActor); - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + [Fact] + public void TestExplicitActorType() + { + var actorType = typeof(RenamedActor); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(); + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - Assert.NotEqual(RenamedActorTypeName, actorType.Name); - Assert.Contains(RenamedActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - } + Assert.NotEqual(RenamedActorTypeName, actorType.Name); + Assert.Contains(RenamedActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + } - [Fact] - public void TestExplicitActorTypeAsParamShouldOverrideInferred() - { - var actorType = typeof(TestActor); - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(ParamActorTypeName); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + [Fact] + public void TestExplicitActorTypeAsParamShouldOverrideInferred() + { + var actorType = typeof(TestActor); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(ParamActorTypeName); + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - Assert.NotEqual(ParamActorTypeName, actorType.Name); - Assert.Contains(ParamActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - } + Assert.NotEqual(ParamActorTypeName, actorType.Name); + Assert.Contains(ParamActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + } - [Fact] - public void TestExplicitActorTypeAsParamShouldOverrideActorAttribute() - { - var actorType = typeof(RenamedActor); - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(ParamActorTypeName); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - - Assert.NotEqual(ParamActorTypeName, actorType.Name); - Assert.NotEqual(ParamActorTypeName, actorType.GetCustomAttribute().TypeName); - Assert.Contains(ParamActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - } + [Fact] + public void TestExplicitActorTypeAsParamShouldOverrideActorAttribute() + { + var actorType = typeof(RenamedActor); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(ParamActorTypeName); + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + + Assert.NotEqual(ParamActorTypeName, actorType.Name); + Assert.NotEqual(ParamActorTypeName, actorType.GetCustomAttribute().TypeName); + Assert.Contains(ParamActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + } - // This tests the change that removed the Activate message from Dapr runtime -> app. - [Fact] - public async Task NoActivateMessageFromRuntime() - { - var actorType = typeof(MyActor); + // This tests the change that removed the Activate message from Dapr runtime -> app. + [Fact] + public async Task NoActivateMessageFromRuntime() + { + var actorType = typeof(MyActor); - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(); + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - var output = new MemoryStream(); - await runtime.DispatchWithoutRemotingAsync(actorType.Name, "abc", nameof(MyActor.MyMethod), new MemoryStream(), output); - var text = Encoding.UTF8.GetString(output.ToArray()); + var output = new MemoryStream(); + await runtime.DispatchWithoutRemotingAsync(actorType.Name, "abc", nameof(MyActor.MyMethod), new MemoryStream(), output); + var text = Encoding.UTF8.GetString(output.ToArray()); - Assert.Equal("\"hi\"", text); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - } + Assert.Equal("\"hi\"", text); + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + } - public interface INotRemotedActor : IActor - { - Task NoArgumentsAsync(); + public interface INotRemotedActor : IActor + { + Task NoArgumentsAsync(); + + Task NoArgumentsWithCancellationAsync(CancellationToken cancellationToken = default); - Task NoArgumentsWithCancellationAsync(CancellationToken cancellationToken = default); + Task SingleArgumentAsync(bool arg); - Task SingleArgumentAsync(bool arg); + Task SingleArgumentWithCancellationAsync(bool arg, CancellationToken cancellationToken = default); + } - Task SingleArgumentWithCancellationAsync(bool arg, CancellationToken cancellationToken = default); + public sealed class NotRemotedActor : Actor, INotRemotedActor + { + public NotRemotedActor(ActorHost host) + : base(host) + { } - public sealed class NotRemotedActor : Actor, INotRemotedActor + public Task NoArgumentsAsync() { - public NotRemotedActor(ActorHost host) - : base(host) - { - } - - public Task NoArgumentsAsync() - { - return Task.FromResult(nameof(NoArgumentsAsync)); - } - - public Task NoArgumentsWithCancellationAsync(CancellationToken cancellationToken = default) - { - return Task.FromResult(nameof(NoArgumentsWithCancellationAsync)); - } - - public Task SingleArgumentAsync(bool arg) - { - return Task.FromResult(nameof(SingleArgumentAsync)); - } - - public Task SingleArgumentWithCancellationAsync(bool arg, CancellationToken cancellationToken = default) - { - return Task.FromResult(nameof(SingleArgumentWithCancellationAsync)); - } + return Task.FromResult(nameof(NoArgumentsAsync)); } - public async Task InvokeMethod(string methodName, object arg = null) where T : Actor + public Task NoArgumentsWithCancellationAsync(CancellationToken cancellationToken = default) { - var options = new ActorRuntimeOptions(); + return Task.FromResult(nameof(NoArgumentsWithCancellationAsync)); + } - options.Actors.RegisterActor(); + public Task SingleArgumentAsync(bool arg) + { + return Task.FromResult(nameof(SingleArgumentAsync)); + } - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + public Task SingleArgumentWithCancellationAsync(bool arg, CancellationToken cancellationToken = default) + { + return Task.FromResult(nameof(SingleArgumentWithCancellationAsync)); + } + } - using var input = new MemoryStream(); + public async Task InvokeMethod(string methodName, object arg = null) where T : Actor + { + var options = new ActorRuntimeOptions(); - if (arg is not null) - { - JsonSerializer.Serialize(input, arg); + options.Actors.RegisterActor(); - input.Seek(0, SeekOrigin.Begin); - } + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - using var output = new MemoryStream(); - - await runtime.DispatchWithoutRemotingAsync(typeof(T).Name, ActorId.CreateRandom().ToString(), methodName, input, output); + using var input = new MemoryStream(); - output.Seek(0, SeekOrigin.Begin); + if (arg is not null) + { + JsonSerializer.Serialize(input, arg); - return JsonSerializer.Deserialize(output); + input.Seek(0, SeekOrigin.Begin); } - [Fact] - public async Task NoRemotingMethodWithNoArguments() - { - string methodName = nameof(INotRemotedActor.NoArgumentsAsync); + using var output = new MemoryStream(); - string result = await InvokeMethod(methodName); + await runtime.DispatchWithoutRemotingAsync(typeof(T).Name, ActorId.CreateRandom().ToString(), methodName, input, output); - Assert.Equal(methodName, result); - } + output.Seek(0, SeekOrigin.Begin); - [Fact] - public async Task NoRemotingMethodWithNoArgumentsWithCancellation() - { - string methodName = nameof(INotRemotedActor.NoArgumentsWithCancellationAsync); + return JsonSerializer.Deserialize(output); + } + + [Fact] + public async Task NoRemotingMethodWithNoArguments() + { + string methodName = nameof(INotRemotedActor.NoArgumentsAsync); - string result = await InvokeMethod(methodName); + string result = await InvokeMethod(methodName); - Assert.Equal(methodName, result); - } + Assert.Equal(methodName, result); + } - [Fact] - public async Task NoRemotingMethodWithSingleArgument() - { - string methodName = nameof(INotRemotedActor.SingleArgumentAsync); + [Fact] + public async Task NoRemotingMethodWithNoArgumentsWithCancellation() + { + string methodName = nameof(INotRemotedActor.NoArgumentsWithCancellationAsync); - string result = await InvokeMethod(methodName, true); + string result = await InvokeMethod(methodName); - Assert.Equal(methodName, result); - } + Assert.Equal(methodName, result); + } - [Fact] - public async Task NoRemotingMethodWithSingleArgumentWithCancellation() - { - string methodName = nameof(INotRemotedActor.SingleArgumentWithCancellationAsync); + [Fact] + public async Task NoRemotingMethodWithSingleArgument() + { + string methodName = nameof(INotRemotedActor.SingleArgumentAsync); - string result = await InvokeMethod(methodName, true); + string result = await InvokeMethod(methodName, true); - Assert.Equal(methodName, result); - } + Assert.Equal(methodName, result); + } - [Fact] - public async Task Actor_UsesCustomActivator() - { - var activator = new TestActivator(); - var actorType = typeof(MyActor); - - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(options => - { - options.Activator = activator; - }); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - - var output = new MemoryStream(); - await runtime.DispatchWithoutRemotingAsync(actorType.Name, "abc", nameof(MyActor.MyMethod), new MemoryStream(), output); - - var text = Encoding.UTF8.GetString(output.ToArray()); - Assert.Equal("\"hi\"", text); - - await runtime.DeactivateAsync(actorType.Name, "abc"); - Assert.Equal(1, activator.CreateCallCount); - Assert.Equal(1, activator.DeleteCallCount); - } + [Fact] + public async Task NoRemotingMethodWithSingleArgumentWithCancellation() + { + string methodName = nameof(INotRemotedActor.SingleArgumentWithCancellationAsync); + + string result = await InvokeMethod(methodName, true); + + Assert.Equal(methodName, result); + } - [Fact] - public async Task TestActorSettings() + [Fact] + public async Task Actor_UsesCustomActivator() + { + var activator = new TestActivator(); + var actorType = typeof(MyActor); + + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(options => { - var actorType = typeof(TestActor); + options.Activator = activator; + }); + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(); - options.ActorIdleTimeout = TimeSpan.FromSeconds(33); - options.ActorScanInterval = TimeSpan.FromSeconds(44); - options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55); - options.DrainRebalancedActors = true; + var output = new MemoryStream(); + await runtime.DispatchWithoutRemotingAsync(actorType.Name, "abc", nameof(MyActor.MyMethod), new MemoryStream(), output); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + var text = Encoding.UTF8.GetString(output.ToArray()); + Assert.Equal("\"hi\"", text); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + await runtime.DeactivateAsync(actorType.Name, "abc"); + Assert.Equal(1, activator.CreateCallCount); + Assert.Equal(1, activator.DeleteCallCount); + } - ArrayBufferWriter writer = new ArrayBufferWriter(); - await runtime.SerializeSettingsAndRegisteredTypes(writer); + [Fact] + public async Task TestActorSettings() + { + var actorType = typeof(TestActor); - // read back the serialized json - var array = writer.WrittenSpan.ToArray(); - string s = Encoding.UTF8.GetString(array, 0, array.Length); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(); + options.ActorIdleTimeout = TimeSpan.FromSeconds(33); + options.ActorScanInterval = TimeSpan.FromSeconds(44); + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55); + options.DrainRebalancedActors = true; - JsonDocument document = JsonDocument.Parse(s); - JsonElement root = document.RootElement; + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - // parse out the entities array - JsonElement element = root.GetProperty("entities"); - Assert.Equal(1, element.GetArrayLength()); + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - JsonElement arrayElement = element[0]; - string actor = arrayElement.GetString(); - Assert.Equal("TestActor", actor); + ArrayBufferWriter writer = new ArrayBufferWriter(); + await runtime.SerializeSettingsAndRegisteredTypes(writer); - // validate the other properties have expected values - element = root.GetProperty("actorIdleTimeout"); - Assert.Equal(TimeSpan.FromSeconds(33), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); + // read back the serialized json + var array = writer.WrittenSpan.ToArray(); + string s = Encoding.UTF8.GetString(array, 0, array.Length); - element = root.GetProperty("actorScanInterval"); - Assert.Equal(TimeSpan.FromSeconds(44), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); + JsonDocument document = JsonDocument.Parse(s); + JsonElement root = document.RootElement; - element = root.GetProperty("drainOngoingCallTimeout"); - Assert.Equal(TimeSpan.FromSeconds(55), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); + // parse out the entities array + JsonElement element = root.GetProperty("entities"); + Assert.Equal(1, element.GetArrayLength()); - element = root.GetProperty("drainRebalancedActors"); - Assert.True(element.GetBoolean()); + JsonElement arrayElement = element[0]; + string actor = arrayElement.GetString(); + Assert.Equal("TestActor", actor); - bool found = root.TryGetProperty("remindersStoragePartitions", out element); - Assert.False(found, "remindersStoragePartitions should not be serialized"); + // validate the other properties have expected values + element = root.GetProperty("actorIdleTimeout"); + Assert.Equal(TimeSpan.FromSeconds(33), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); - JsonElement jsonValue; - Assert.False(root.GetProperty("reentrancy").TryGetProperty("maxStackDepth", out jsonValue)); - } + element = root.GetProperty("actorScanInterval"); + Assert.Equal(TimeSpan.FromSeconds(44), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); - [Fact] - public async Task TestActorSettingsWithRemindersStoragePartitions() - { - var actorType = typeof(TestActor); + element = root.GetProperty("drainOngoingCallTimeout"); + Assert.Equal(TimeSpan.FromSeconds(55), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(); - options.RemindersStoragePartitions = 12; + element = root.GetProperty("drainRebalancedActors"); + Assert.True(element.GetBoolean()); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + bool found = root.TryGetProperty("remindersStoragePartitions", out element); + Assert.False(found, "remindersStoragePartitions should not be serialized"); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + JsonElement jsonValue; + Assert.False(root.GetProperty("reentrancy").TryGetProperty("maxStackDepth", out jsonValue)); + } - ArrayBufferWriter writer = new ArrayBufferWriter(); - await runtime.SerializeSettingsAndRegisteredTypes(writer); + [Fact] + public async Task TestActorSettingsWithRemindersStoragePartitions() + { + var actorType = typeof(TestActor); - // read back the serialized json - var array = writer.WrittenSpan.ToArray(); - string s = Encoding.UTF8.GetString(array, 0, array.Length); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(); + options.RemindersStoragePartitions = 12; - JsonDocument document = JsonDocument.Parse(s); - JsonElement root = document.RootElement; + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - // parse out the entities array - JsonElement element = root.GetProperty("entities"); - Assert.Equal(1, element.GetArrayLength()); + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - JsonElement arrayElement = element[0]; - string actor = arrayElement.GetString(); - Assert.Equal("TestActor", actor); + ArrayBufferWriter writer = new ArrayBufferWriter(); + await runtime.SerializeSettingsAndRegisteredTypes(writer); - element = root.GetProperty("remindersStoragePartitions"); - Assert.Equal(12, element.GetInt64()); - } + // read back the serialized json + var array = writer.WrittenSpan.ToArray(); + string s = Encoding.UTF8.GetString(array, 0, array.Length); - [Fact] - public async Task TestActorSettingsWithReentrancy() - { - var actorType = typeof(TestActor); + JsonDocument document = JsonDocument.Parse(s); + JsonElement root = document.RootElement; - var options = new ActorRuntimeOptions(); - options.Actors.RegisterActor(); - options.ActorIdleTimeout = TimeSpan.FromSeconds(33); - options.ActorScanInterval = TimeSpan.FromSeconds(44); - options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55); - options.DrainRebalancedActors = true; - options.ReentrancyConfig.Enabled = true; - options.ReentrancyConfig.MaxStackDepth = 64; + // parse out the entities array + JsonElement element = root.GetProperty("entities"); + Assert.Equal(1, element.GetArrayLength()); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + JsonElement arrayElement = element[0]; + string actor = arrayElement.GetString(); + Assert.Equal("TestActor", actor); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + element = root.GetProperty("remindersStoragePartitions"); + Assert.Equal(12, element.GetInt64()); + } - ArrayBufferWriter writer = new ArrayBufferWriter(); - await runtime.SerializeSettingsAndRegisteredTypes(writer); + [Fact] + public async Task TestActorSettingsWithReentrancy() + { + var actorType = typeof(TestActor); - // read back the serialized json - var array = writer.WrittenSpan.ToArray(); - string s = Encoding.UTF8.GetString(array, 0, array.Length); + var options = new ActorRuntimeOptions(); + options.Actors.RegisterActor(); + options.ActorIdleTimeout = TimeSpan.FromSeconds(33); + options.ActorScanInterval = TimeSpan.FromSeconds(44); + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55); + options.DrainRebalancedActors = true; + options.ReentrancyConfig.Enabled = true; + options.ReentrancyConfig.MaxStackDepth = 64; - JsonDocument document = JsonDocument.Parse(s); - JsonElement root = document.RootElement; + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - // parse out the entities array - JsonElement element = root.GetProperty("entities"); - Assert.Equal(1, element.GetArrayLength()); + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - element = root.GetProperty("reentrancy").GetProperty("enabled"); - Assert.True(element.GetBoolean()); + ArrayBufferWriter writer = new ArrayBufferWriter(); + await runtime.SerializeSettingsAndRegisteredTypes(writer); - element = root.GetProperty("reentrancy").GetProperty("maxStackDepth"); - Assert.Equal(64, element.GetInt32()); - } + // read back the serialized json + var array = writer.WrittenSpan.ToArray(); + string s = Encoding.UTF8.GetString(array, 0, array.Length); - [Fact] - public async Task TestActorSettingsWithPerActorConfigurations() - { - var actorType = typeof(TestActor); - var options = new ActorRuntimeOptions(); - options.ActorIdleTimeout = TimeSpan.FromSeconds(33); - options.ActorScanInterval = TimeSpan.FromSeconds(44); - options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55); - options.DrainRebalancedActors = true; - options.ReentrancyConfig.Enabled = true; - options.ReentrancyConfig.MaxStackDepth = 32; - options.Actors.RegisterActor(options); + JsonDocument document = JsonDocument.Parse(s); + JsonElement root = document.RootElement; + + // parse out the entities array + JsonElement element = root.GetProperty("entities"); + Assert.Equal(1, element.GetArrayLength()); - var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); + element = root.GetProperty("reentrancy").GetProperty("enabled"); + Assert.True(element.GetBoolean()); - Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); + element = root.GetProperty("reentrancy").GetProperty("maxStackDepth"); + Assert.Equal(64, element.GetInt32()); + } - ArrayBufferWriter writer = new ArrayBufferWriter(); - await runtime.SerializeSettingsAndRegisteredTypes(writer); + [Fact] + public async Task TestActorSettingsWithPerActorConfigurations() + { + var actorType = typeof(TestActor); + var options = new ActorRuntimeOptions(); + options.ActorIdleTimeout = TimeSpan.FromSeconds(33); + options.ActorScanInterval = TimeSpan.FromSeconds(44); + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55); + options.DrainRebalancedActors = true; + options.ReentrancyConfig.Enabled = true; + options.ReentrancyConfig.MaxStackDepth = 32; + options.Actors.RegisterActor(options); - // read back the serialized json - var array = writer.WrittenSpan.ToArray(); - string s = Encoding.UTF8.GetString(array, 0, array.Length); + var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory); - JsonDocument document = JsonDocument.Parse(s); - JsonElement root = document.RootElement; + Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture); - JsonElement element = root.GetProperty("entities"); - Assert.Equal(1, element.GetArrayLength()); + ArrayBufferWriter writer = new ArrayBufferWriter(); + await runtime.SerializeSettingsAndRegisteredTypes(writer); - element = root.GetProperty("entitiesConfig"); - Assert.Equal(1, element.GetArrayLength()); + // read back the serialized json + var array = writer.WrittenSpan.ToArray(); + string s = Encoding.UTF8.GetString(array, 0, array.Length); - var perEntityConfig = element[0]; + JsonDocument document = JsonDocument.Parse(s); + JsonElement root = document.RootElement; - element = perEntityConfig.GetProperty("actorIdleTimeout"); - Assert.Equal(TimeSpan.FromSeconds(33), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); + JsonElement element = root.GetProperty("entities"); + Assert.Equal(1, element.GetArrayLength()); - element = perEntityConfig.GetProperty("actorScanInterval"); - Assert.Equal(TimeSpan.FromSeconds(44), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); + element = root.GetProperty("entitiesConfig"); + Assert.Equal(1, element.GetArrayLength()); - element = perEntityConfig.GetProperty("drainOngoingCallTimeout"); - Assert.Equal(TimeSpan.FromSeconds(55), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); + var perEntityConfig = element[0]; - element = perEntityConfig.GetProperty("drainRebalancedActors"); - Assert.True(element.GetBoolean()); + element = perEntityConfig.GetProperty("actorIdleTimeout"); + Assert.Equal(TimeSpan.FromSeconds(33), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); - element = root.GetProperty("reentrancy").GetProperty("enabled"); - Assert.True(element.GetBoolean()); + element = perEntityConfig.GetProperty("actorScanInterval"); + Assert.Equal(TimeSpan.FromSeconds(44), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); - element = root.GetProperty("reentrancy").GetProperty("maxStackDepth"); - Assert.Equal(32, element.GetInt32()); - } + element = perEntityConfig.GetProperty("drainOngoingCallTimeout"); + Assert.Equal(TimeSpan.FromSeconds(55), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString())); - private sealed class TestActor : Actor, ITestActor + element = perEntityConfig.GetProperty("drainRebalancedActors"); + Assert.True(element.GetBoolean()); + + element = root.GetProperty("reentrancy").GetProperty("enabled"); + Assert.True(element.GetBoolean()); + + element = root.GetProperty("reentrancy").GetProperty("maxStackDepth"); + Assert.Equal(32, element.GetInt32()); + } + + private sealed class TestActor : Actor, ITestActor + { + public TestActor(ActorHost host) + : base(host) { - public TestActor(ActorHost host) - : base(host) - { - } } + } - [Actor(TypeName = RenamedActorTypeName)] - private sealed class RenamedActor : Actor, ITestActor + [Actor(TypeName = RenamedActorTypeName)] + private sealed class RenamedActor : Actor, ITestActor + { + public RenamedActor(ActorHost host) + : base(host) { - public RenamedActor(ActorHost host) - : base(host) - { - } } + } + + private interface IAnotherActor : IActor + { + public Task MyMethod(); + } - private interface IAnotherActor : IActor + private sealed class MyActor : Actor, IAnotherActor + { + public MyActor(ActorHost host) + : base(host) { - public Task MyMethod(); } - private sealed class MyActor : Actor, IAnotherActor + public Task MyMethod() { - public MyActor(ActorHost host) - : base(host) - { - } - - public Task MyMethod() - { - return Task.FromResult("hi"); - } + return Task.FromResult("hi"); } + } - private class TestActivator : DefaultActorActivator - { - public int CreateCallCount { get; set; } + private class TestActivator : DefaultActorActivator + { + public int CreateCallCount { get; set; } - public int DeleteCallCount { get; set; } + public int DeleteCallCount { get; set; } - public override Task CreateAsync(ActorHost host) - { - CreateCallCount++;; - return base.CreateAsync(host); - } + public override Task CreateAsync(ActorHost host) + { + CreateCallCount++;; + return base.CreateAsync(host); + } - public override Task DeleteAsync(ActorActivatorState state) - { - DeleteCallCount++; - return base.DeleteAsync(state); - } + public override Task DeleteAsync(ActorActivatorState state) + { + DeleteCallCount++; + return base.DeleteAsync(state); } } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Runtime/ActorTypeInformationTests.cs b/test/Dapr.Actors.Test/Runtime/ActorTypeInformationTests.cs index 74b407c73..25f02fbf8 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorTypeInformationTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorTypeInformationTests.cs @@ -11,72 +11,71 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.Reflection; +using Dapr.Actors; +using Dapr.Actors.Runtime; +using Xunit; + +public sealed class ActorTypeInformationTests { - using System; - using System.Reflection; - using Dapr.Actors; - using Dapr.Actors.Runtime; - using Xunit; + private const string RenamedActorTypeName = "MyRenamedActor"; + private const string ParamActorTypeName = "AnotherRenamedActor"; - public sealed class ActorTypeInformationTests + private interface ITestActor : IActor { - private const string RenamedActorTypeName = "MyRenamedActor"; - private const string ParamActorTypeName = "AnotherRenamedActor"; - - private interface ITestActor : IActor - { - } + } - [Fact] - public void TestInferredActorType() - { - var actorType = typeof(TestActor); - var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); + [Fact] + public void TestInferredActorType() + { + var actorType = typeof(TestActor); + var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); - Assert.Equal(actorType.Name, actorTypeInformation.ActorTypeName); - } + Assert.Equal(actorType.Name, actorTypeInformation.ActorTypeName); + } - [Fact] - public void TestExplicitActorType() - { - var actorType = typeof(RenamedActor); + [Fact] + public void TestExplicitActorType() + { + var actorType = typeof(RenamedActor); - Assert.NotEqual(RenamedActorTypeName, actorType.Name); + Assert.NotEqual(RenamedActorTypeName, actorType.Name); - var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); + var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); - Assert.Equal(RenamedActorTypeName, actorTypeInformation.ActorTypeName); - } + Assert.Equal(RenamedActorTypeName, actorTypeInformation.ActorTypeName); + } - [Theory] - [InlineData(typeof(TestActor))] - [InlineData(typeof(RenamedActor))] - public void TestExplicitActorTypeAsParam(Type actorType) - { - Assert.NotEqual(ParamActorTypeName, actorType.Name); - Assert.NotEqual(ParamActorTypeName, actorType.GetCustomAttribute()?.TypeName); + [Theory] + [InlineData(typeof(TestActor))] + [InlineData(typeof(RenamedActor))] + public void TestExplicitActorTypeAsParam(Type actorType) + { + Assert.NotEqual(ParamActorTypeName, actorType.Name); + Assert.NotEqual(ParamActorTypeName, actorType.GetCustomAttribute()?.TypeName); - var actorTypeInformation = ActorTypeInformation.Get(actorType, ParamActorTypeName); + var actorTypeInformation = ActorTypeInformation.Get(actorType, ParamActorTypeName); - Assert.Equal(ParamActorTypeName, actorTypeInformation.ActorTypeName); - } + Assert.Equal(ParamActorTypeName, actorTypeInformation.ActorTypeName); + } - private sealed class TestActor : Actor, ITestActor + private sealed class TestActor : Actor, ITestActor + { + public TestActor(ActorHost host) + : base(host) { - public TestActor(ActorHost host) - : base(host) - { - } } + } - [Actor(TypeName = RenamedActorTypeName)] - private sealed class RenamedActor : Actor, ITestActor + [Actor(TypeName = RenamedActorTypeName)] + private sealed class RenamedActor : Actor, ITestActor + { + public RenamedActor(ActorHost host) + : base(host) { - public RenamedActor(ActorHost host) - : base(host) - { - } } } -} +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs b/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs index 87045b295..9861a1260 100644 --- a/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs +++ b/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs @@ -15,101 +15,100 @@ using System.Threading.Tasks; using Xunit; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +public class DefaultActorActivatorTests { - public class DefaultActorActivatorTests + [Fact] + public async Task CreateAsync_CallsConstructor() { - [Fact] - public async Task CreateAsync_CallsConstructor() - { - var activator = new DefaultActorActivator(); + var activator = new DefaultActorActivator(); - var host = ActorHost.CreateForTest(); - var state = await activator.CreateAsync(host); - Assert.IsType(state.Actor); - } + var host = ActorHost.CreateForTest(); + var state = await activator.CreateAsync(host); + Assert.IsType(state.Actor); + } - [Fact] - public async Task DeleteAsync_NotDisposable() - { - var activator = new DefaultActorActivator(); + [Fact] + public async Task DeleteAsync_NotDisposable() + { + var activator = new DefaultActorActivator(); - var host = ActorHost.CreateForTest(); - var actor = new TestActor(host); - var state = new ActorActivatorState(actor); + var host = ActorHost.CreateForTest(); + var actor = new TestActor(host); + var state = new ActorActivatorState(actor); - await activator.DeleteAsync(state); // does not throw - } + await activator.DeleteAsync(state); // does not throw + } - [Fact] - public async Task DeleteAsync_Disposable() - { - var activator = new DefaultActorActivator(); + [Fact] + public async Task DeleteAsync_Disposable() + { + var activator = new DefaultActorActivator(); - var host = ActorHost.CreateForTest(); - var actor = new DisposableActor(host); - var state = new ActorActivatorState(actor); + var host = ActorHost.CreateForTest(); + var actor = new DisposableActor(host); + var state = new ActorActivatorState(actor); - await activator.DeleteAsync(state); // does not throw + await activator.DeleteAsync(state); // does not throw - Assert.True(actor.IsDisposed); - } + Assert.True(actor.IsDisposed); + } - [Fact] - public async Task DeleteAsync_AsyncDisposable() - { - var activator = new DefaultActorActivator(); + [Fact] + public async Task DeleteAsync_AsyncDisposable() + { + var activator = new DefaultActorActivator(); - var host = ActorHost.CreateForTest(); - var actor = new AsyncDisposableActor(host); - var state = new ActorActivatorState(actor); + var host = ActorHost.CreateForTest(); + var actor = new AsyncDisposableActor(host); + var state = new ActorActivatorState(actor); - await activator.DeleteAsync(state); + await activator.DeleteAsync(state); - Assert.True(actor.IsDisposed); - } + Assert.True(actor.IsDisposed); + } + + private interface ITestActor : IActor + { + } - private interface ITestActor : IActor + private class TestActor : Actor, ITestActor + { + public TestActor(ActorHost host) + : base(host) { } + } - private class TestActor : Actor, ITestActor + private class DisposableActor : Actor, ITestActor, IDisposable + { + public DisposableActor(ActorHost host) + : base(host) { - public TestActor(ActorHost host) - : base(host) - { - } } - private class DisposableActor : Actor, ITestActor, IDisposable - { - public DisposableActor(ActorHost host) - : base(host) - { - } + public bool IsDisposed { get; set; } - public bool IsDisposed { get; set; } + public void Dispose() + { + IsDisposed = true; + } + } - public void Dispose() - { - IsDisposed = true; - } + private class AsyncDisposableActor : Actor, ITestActor, IAsyncDisposable + { + public AsyncDisposableActor(ActorHost host) + : base(host) + { } - private class AsyncDisposableActor : Actor, ITestActor, IAsyncDisposable + public bool IsDisposed { get; set; } + + public ValueTask DisposeAsync() { - public AsyncDisposableActor(ActorHost host) - : base(host) - { - } - - public bool IsDisposed { get; set; } - - public ValueTask DisposeAsync() - { - IsDisposed = true; - return new ValueTask(); - } + IsDisposed = true; + return new ValueTask(); } } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest.App/CustomTopicAttribute.cs b/test/Dapr.AspNetCore.IntegrationTest.App/CustomTopicAttribute.cs index 6f65f060f..1888ca36c 100644 --- a/test/Dapr.AspNetCore.IntegrationTest.App/CustomTopicAttribute.cs +++ b/test/Dapr.AspNetCore.IntegrationTest.App/CustomTopicAttribute.cs @@ -11,24 +11,23 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest.App -{ - using System; +namespace Dapr.AspNetCore.IntegrationTest.App; + +using System; - public class CustomTopicAttribute : Attribute, ITopicMetadata +public class CustomTopicAttribute : Attribute, ITopicMetadata +{ + public CustomTopicAttribute(string pubsubName, string name) { - public CustomTopicAttribute(string pubsubName, string name) - { - this.Name = "custom-" + name; - this.PubsubName = "custom-" + pubsubName; - } + this.Name = "custom-" + name; + this.PubsubName = "custom-" + pubsubName; + } - public string Name { get; } + public string Name { get; } - public string PubsubName { get; } + public string PubsubName { get; } - public new string Match { get; } + public new string Match { get; } - public int Priority { get; } - } -} + public int Priority { get; } +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest.App/DaprController.cs b/test/Dapr.AspNetCore.IntegrationTest.App/DaprController.cs index 13f72ff70..31f31b017 100644 --- a/test/Dapr.AspNetCore.IntegrationTest.App/DaprController.cs +++ b/test/Dapr.AspNetCore.IntegrationTest.App/DaprController.cs @@ -11,164 +11,163 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest.App +namespace Dapr.AspNetCore.IntegrationTest.App; + +using System.Text; +using System.Threading.Tasks; +using Dapr; +using Dapr.Client; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.WebUtilities; + +[ApiController] +public class DaprController : ControllerBase { - using System.Text; - using System.Threading.Tasks; - using Dapr; - using Dapr.Client; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.WebUtilities; - - [ApiController] - public class DaprController : ControllerBase - { - [Topic("pubsub", "B")] - [HttpPost("/B")] - public void TopicB() - { - } - - [CustomTopic("pubsub", "C")] - [HttpPost("/C")] - public void TopicC() - { - } - - [Topic("pubsub", "D", true)] - [HttpPost("/D")] - public void TopicD() - { - } - - [Topic("pubsub", "E", false)] - [HttpPost("/E")] - public void TopicE() - { - } - - [Topic("pubsub", "E", false, "event.type == \"critical\"", 1)] - [HttpPost("/E-Critical")] - public void TopicECritical() - { - } - - [Topic("pubsub", "E", false, "event.type == \"important\"", 2)] - [HttpPost("/E-Important")] - public void TopicEImportant() - { - } - - [BulkSubscribe("F")] - [Topic("pubsub", "F")] - [Topic("pubsub", "F.1", true)] - [HttpPost("/multiTopicAttr")] - public void MultipleTopics() - { - } - - [BulkSubscribe("G", 300)] - [Topic("pubsub", "G", "deadLetterTopicName", false)] - [HttpPost("/G")] - public void TopicG() - { - } - - [BulkSubscribe("metadata.1", 500, 2000)] - [Topic("pubsub", "metadata", new string[1] { "id1" })] - [Topic("pubsub", "metadata.1", true)] - [HttpPost("/multiMetadataTopicAttr")] - [TopicMetadata("n1", "v1")] - [TopicMetadata("id1", "n2", "v2")] - [TopicMetadata("id1", "n2", "v3")] - public void MultipleMetadataTopics() - { - } - - [Topic("pubsub", "metadataseparator", metadataSeparator: "|")] - [HttpPost("/topicmetadataseparatorattr")] - [TopicMetadata("n1", "v1")] - [TopicMetadata("n1", "v2")] - public void TopicMetadataSeparator() - { - } - - [Topic("pubsub", "metadataseparatorbyemptytring")] - [HttpPost("/topicmetadataseparatorattrbyemptytring")] - [TopicMetadata("n1", "v1")] - [TopicMetadata("n1", "")] - public void TopicMetadataSeparatorByemptytring () - { - } - - [Topic("pubsub", "splitTopicAttr", true)] - [HttpPost("/splitTopics")] - public void SplitTopic() - { - } - - [Topic("pubsub", "register-user")] - [HttpPost("/register-user")] - public ActionResult RegisterUser(UserInfo user) - { - return user; // echo back the user for testing - } - - [Topic("pubsub", "register-user-plaintext")] - [HttpPost("/register-user-plaintext")] - public async Task RegisterUserPlaintext() - { - using var reader = new HttpRequestStreamReader(Request.Body, Encoding.UTF8); - var user = await reader.ReadToEndAsync(); - return Content(user, "text/plain"); // echo back the user for testing - } - - [HttpPost("/controllerwithoutstateentry/{widget}")] - public async Task AddOneWithoutStateEntry([FromServices] DaprClient state, [FromState("testStore")] Widget widget) - { - widget.Count++; - await state.SaveStateAsync("testStore", (string)this.HttpContext.Request.RouteValues["widget"], widget); - } - - [HttpPost("/controllerwithstateentry/{widget}")] - public async Task AddOneWithStateEntry([FromState("testStore")] StateEntry widget) - { - widget.Value.Count++; - await widget.SaveAsync(); - } - - [HttpPost("/controllerwithstateentryandcustomkey/{widget}")] - public async Task AddOneWithStateEntryAndCustomKey([FromState("testStore", "widget")] StateEntry state) - { - state.Value.Count++; - await state.SaveAsync(); - } - - [HttpPost("/echo-user")] - public ActionResult EchoUser([FromQuery] UserInfo user) - { - // To simulate an action where there's no Dapr attribute, yet MVC still checks the list of available model binder providers. - return user; - } - - [HttpGet("controllerwithoutstateentry/{widget}")] - public ActionResult Get([FromState("testStore")] Widget widget) - { - return widget; - } - - [HttpGet("controllerwithstateentry/{widgetStateEntry}")] - public ActionResult Get([FromState("testStore")] StateEntry widgetStateEntry) - { - return widgetStateEntry.Value; - } - - [Authorize("Dapr")] - [HttpPost("/requires-api-token")] - public ActionResult RequiresApiToken(UserInfo user) - { - return user; - } - } -} + [Topic("pubsub", "B")] + [HttpPost("/B")] + public void TopicB() + { + } + + [CustomTopic("pubsub", "C")] + [HttpPost("/C")] + public void TopicC() + { + } + + [Topic("pubsub", "D", true)] + [HttpPost("/D")] + public void TopicD() + { + } + + [Topic("pubsub", "E", false)] + [HttpPost("/E")] + public void TopicE() + { + } + + [Topic("pubsub", "E", false, "event.type == \"critical\"", 1)] + [HttpPost("/E-Critical")] + public void TopicECritical() + { + } + + [Topic("pubsub", "E", false, "event.type == \"important\"", 2)] + [HttpPost("/E-Important")] + public void TopicEImportant() + { + } + + [BulkSubscribe("F")] + [Topic("pubsub", "F")] + [Topic("pubsub", "F.1", true)] + [HttpPost("/multiTopicAttr")] + public void MultipleTopics() + { + } + + [BulkSubscribe("G", 300)] + [Topic("pubsub", "G", "deadLetterTopicName", false)] + [HttpPost("/G")] + public void TopicG() + { + } + + [BulkSubscribe("metadata.1", 500, 2000)] + [Topic("pubsub", "metadata", new string[1] { "id1" })] + [Topic("pubsub", "metadata.1", true)] + [HttpPost("/multiMetadataTopicAttr")] + [TopicMetadata("n1", "v1")] + [TopicMetadata("id1", "n2", "v2")] + [TopicMetadata("id1", "n2", "v3")] + public void MultipleMetadataTopics() + { + } + + [Topic("pubsub", "metadataseparator", metadataSeparator: "|")] + [HttpPost("/topicmetadataseparatorattr")] + [TopicMetadata("n1", "v1")] + [TopicMetadata("n1", "v2")] + public void TopicMetadataSeparator() + { + } + + [Topic("pubsub", "metadataseparatorbyemptytring")] + [HttpPost("/topicmetadataseparatorattrbyemptytring")] + [TopicMetadata("n1", "v1")] + [TopicMetadata("n1", "")] + public void TopicMetadataSeparatorByemptytring () + { + } + + [Topic("pubsub", "splitTopicAttr", true)] + [HttpPost("/splitTopics")] + public void SplitTopic() + { + } + + [Topic("pubsub", "register-user")] + [HttpPost("/register-user")] + public ActionResult RegisterUser(UserInfo user) + { + return user; // echo back the user for testing + } + + [Topic("pubsub", "register-user-plaintext")] + [HttpPost("/register-user-plaintext")] + public async Task RegisterUserPlaintext() + { + using var reader = new HttpRequestStreamReader(Request.Body, Encoding.UTF8); + var user = await reader.ReadToEndAsync(); + return Content(user, "text/plain"); // echo back the user for testing + } + + [HttpPost("/controllerwithoutstateentry/{widget}")] + public async Task AddOneWithoutStateEntry([FromServices] DaprClient state, [FromState("testStore")] Widget widget) + { + widget.Count++; + await state.SaveStateAsync("testStore", (string)this.HttpContext.Request.RouteValues["widget"], widget); + } + + [HttpPost("/controllerwithstateentry/{widget}")] + public async Task AddOneWithStateEntry([FromState("testStore")] StateEntry widget) + { + widget.Value.Count++; + await widget.SaveAsync(); + } + + [HttpPost("/controllerwithstateentryandcustomkey/{widget}")] + public async Task AddOneWithStateEntryAndCustomKey([FromState("testStore", "widget")] StateEntry state) + { + state.Value.Count++; + await state.SaveAsync(); + } + + [HttpPost("/echo-user")] + public ActionResult EchoUser([FromQuery] UserInfo user) + { + // To simulate an action where there's no Dapr attribute, yet MVC still checks the list of available model binder providers. + return user; + } + + [HttpGet("controllerwithoutstateentry/{widget}")] + public ActionResult Get([FromState("testStore")] Widget widget) + { + return widget; + } + + [HttpGet("controllerwithstateentry/{widgetStateEntry}")] + public ActionResult Get([FromState("testStore")] StateEntry widgetStateEntry) + { + return widgetStateEntry.Value; + } + + [Authorize("Dapr")] + [HttpPost("/requires-api-token")] + public ActionResult RequiresApiToken(UserInfo user) + { + return user; + } +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest.App/Program.cs b/test/Dapr.AspNetCore.IntegrationTest.App/Program.cs index 39ef84bc6..f6f13dd6b 100644 --- a/test/Dapr.AspNetCore.IntegrationTest.App/Program.cs +++ b/test/Dapr.AspNetCore.IntegrationTest.App/Program.cs @@ -11,23 +11,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest.App -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; +namespace Dapr.AspNetCore.IntegrationTest.App; - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); +public class Program +{ + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); } -} + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest.App/Startup.cs b/test/Dapr.AspNetCore.IntegrationTest.App/Startup.cs index e776fa463..75e384d26 100644 --- a/test/Dapr.AspNetCore.IntegrationTest.App/Startup.cs +++ b/test/Dapr.AspNetCore.IntegrationTest.App/Startup.cs @@ -11,71 +11,70 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest.App +namespace Dapr.AspNetCore.IntegrationTest.App; + +using System.Collections.Generic; +using System.Threading.Tasks; +using Dapr.Client; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +public class Startup { - using System.Collections.Generic; - using System.Threading.Tasks; - using Dapr.Client; - using Microsoft.AspNetCore.Authentication; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - public void ConfigureServices(IServiceCollection services) - { - services.AddAuthentication().AddDapr(options => options.Token = "abcdefg"); + public void ConfigureServices(IServiceCollection services) + { + services.AddAuthentication().AddDapr(options => options.Token = "abcdefg"); - services.AddAuthorization(o => o.AddDapr()); + services.AddAuthorization(o => o.AddDapr()); - services.AddControllers().AddDapr(); - } + services.AddControllers().AddDapr(); + } - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseAuthentication(); + app.UseAuthentication(); - app.UseAuthorization(); + app.UseAuthorization(); - app.UseCloudEvents(); + app.UseCloudEvents(); - app.UseEndpoints(endpoints => - { - endpoints.MapSubscribeHandler(); - endpoints.MapControllers(); + app.UseEndpoints(endpoints => + { + endpoints.MapSubscribeHandler(); + endpoints.MapControllers(); - endpoints.MapPost("/topic-a", context => Task.CompletedTask).WithTopic("testpubsub", "A").WithTopic("testpubsub", "A.1"); + endpoints.MapPost("/topic-a", context => Task.CompletedTask).WithTopic("testpubsub", "A").WithTopic("testpubsub", "A.1"); - endpoints.MapPost("/splitTopics", context => Task.CompletedTask).WithTopic("pubsub", "splitTopicBuilder"); + endpoints.MapPost("/splitTopics", context => Task.CompletedTask).WithTopic("pubsub", "splitTopicBuilder"); - endpoints.MapPost("/splitMetadataTopics", context => Task.CompletedTask).WithTopic("pubsub", "splitMetadataTopicBuilder", new Dictionary { { "n1", "v1" }, { "n2", "v1" } }); + endpoints.MapPost("/splitMetadataTopics", context => Task.CompletedTask).WithTopic("pubsub", "splitMetadataTopicBuilder", new Dictionary { { "n1", "v1" }, { "n2", "v1" } }); - endpoints.MapPost("/routingwithstateentry/{widget}", async context => - { - var daprClient = context.RequestServices.GetRequiredService(); - var state = await daprClient.GetStateEntryAsync("testStore", (string)context.Request.RouteValues["widget"]); - state.Value.Count++; - await state.SaveAsync(); - }); + endpoints.MapPost("/routingwithstateentry/{widget}", async context => + { + var daprClient = context.RequestServices.GetRequiredService(); + var state = await daprClient.GetStateEntryAsync("testStore", (string)context.Request.RouteValues["widget"]); + state.Value.Count++; + await state.SaveAsync(); }); - } + }); } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest.App/UserInfo.cs b/test/Dapr.AspNetCore.IntegrationTest.App/UserInfo.cs index 51fa1eb0b..54eba52ee 100644 --- a/test/Dapr.AspNetCore.IntegrationTest.App/UserInfo.cs +++ b/test/Dapr.AspNetCore.IntegrationTest.App/UserInfo.cs @@ -11,13 +11,12 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest.App -{ - using System.ComponentModel.DataAnnotations; +namespace Dapr.AspNetCore.IntegrationTest.App; + +using System.ComponentModel.DataAnnotations; - public class UserInfo - { - [Required] - public string Name { get; set; } - } +public class UserInfo +{ + [Required] + public string Name { get; set; } } \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest.App/Widget.cs b/test/Dapr.AspNetCore.IntegrationTest.App/Widget.cs index aecec6ee1..3177ef089 100644 --- a/test/Dapr.AspNetCore.IntegrationTest.App/Widget.cs +++ b/test/Dapr.AspNetCore.IntegrationTest.App/Widget.cs @@ -11,12 +11,11 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest.App +namespace Dapr.AspNetCore.IntegrationTest.App; + +public class Widget { - public class Widget - { - public string Size { get; set; } + public string Size { get; set; } - public int Count { get; set; } - } + public int Count { get; set; } } \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs b/test/Dapr.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs index 1eb494cae..24df9b66d 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs @@ -11,30 +11,29 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest +namespace Dapr.AspNetCore.IntegrationTest; + +using Dapr.AspNetCore.IntegrationTest.App; +using Dapr.Client; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +public class AppWebApplicationFactory : WebApplicationFactory { - using Dapr.AspNetCore.IntegrationTest.App; - using Dapr.Client; - using Microsoft.AspNetCore.Mvc.Testing; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; + internal StateTestClient DaprClient { get; } = new StateTestClient(); - public class AppWebApplicationFactory : WebApplicationFactory + protected override IHostBuilder CreateHostBuilder() { - internal StateTestClient DaprClient { get; } = new StateTestClient(); - - protected override IHostBuilder CreateHostBuilder() + var builder = base.CreateHostBuilder(); + builder.ConfigureLogging(b => + { + b.SetMinimumLevel(LogLevel.None); + }); + return builder.ConfigureServices((context, services) => { - var builder = base.CreateHostBuilder(); - builder.ConfigureLogging(b => - { - b.SetMinimumLevel(LogLevel.None); - }); - return builder.ConfigureServices((context, services) => - { - services.AddSingleton(this.DaprClient); - }); - } + services.AddSingleton(this.DaprClient); + }); } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs index 3684e709c..ba91828fc 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs @@ -20,52 +20,51 @@ using Newtonsoft.Json; using Xunit; -namespace Dapr.AspNetCore.IntegrationTest +namespace Dapr.AspNetCore.IntegrationTest; + +public class AuthenticationTest { - public class AuthenticationTest + [Fact] + public async Task ValidToken_ShouldBeAuthenticatedAndAuthorized() { - [Fact] - public async Task ValidToken_ShouldBeAuthenticatedAndAuthorized() + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) + var userInfo = new UserInfo + { + Name = "jimmy" + }; + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/requires-api-token") { - var userInfo = new UserInfo - { - Name = "jimmy" - }; - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/requires-api-token") - { - Content = new StringContent(JsonConvert.SerializeObject(userInfo), Encoding.UTF8, "application/json") - }; - request.Headers.Add("Dapr-Api-Token", "abcdefg"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - var responseUserInfo = JsonConvert.DeserializeObject(responseContent); - responseUserInfo.Name.ShouldBe(userInfo.Name); - } + Content = new StringContent(JsonConvert.SerializeObject(userInfo), Encoding.UTF8, "application/json") + }; + request.Headers.Add("Dapr-Api-Token", "abcdefg"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync(); + var responseUserInfo = JsonConvert.DeserializeObject(responseContent); + responseUserInfo.Name.ShouldBe(userInfo.Name); } + } - [Fact] - public async Task InvalidToken_ShouldBeUnauthorized() + [Fact] + public async Task InvalidToken_ShouldBeUnauthorized() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) + var userInfo = new UserInfo + { + Name = "jimmy" + }; + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/requires-api-token") { - var userInfo = new UserInfo - { - Name = "jimmy" - }; - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/requires-api-token") - { - Content = new StringContent(JsonConvert.SerializeObject(userInfo), Encoding.UTF8, "application/json") - }; - request.Headers.Add("Dapr-Api-Token", "asdfgh"); - var response = await httpClient.SendAsync(request); + Content = new StringContent(JsonConvert.SerializeObject(userInfo), Encoding.UTF8, "application/json") + }; + request.Headers.Add("Dapr-Api-Token", "asdfgh"); + var response = await httpClient.SendAsync(request); - response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized); - } + response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized); } } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs index 928286fb6..dde6751cf 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs @@ -11,54 +11,54 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest +namespace Dapr.AspNetCore.IntegrationTest; + +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Dapr.AspNetCore.IntegrationTest.App; +using Shouldly; +using Xunit; + +public class CloudEventsIntegrationTest { - using System.Net.Http; - using System.Net.Http.Headers; - using System.Text; - using System.Text.Json; - using System.Threading.Tasks; - using Dapr.AspNetCore.IntegrationTest.App; - using Shouldly; - using Xunit; - - public class CloudEventsIntegrationTest + private readonly JsonSerializerOptions options = new JsonSerializerOptions() { - private readonly JsonSerializerOptions options = new JsonSerializerOptions() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true, - }; + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + }; - [Fact] - public async Task CanSendEmptyStructuredCloudEvent() + [Fact] + public async Task CanSendEmptyStructuredCloudEvent() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/B") - { - Content = new StringContent("{}", Encoding.UTF8) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/B") + { + Content = new StringContent("{}", Encoding.UTF8) + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - } + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task CanSendStructuredCloudEvent() + [Fact] + public async Task CanSendStructuredCloudEvent() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user") - { - Content = new StringContent( + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user") + { + Content = new StringContent( JsonSerializer.Serialize( new { @@ -68,27 +68,27 @@ public async Task CanSendStructuredCloudEvent() }, }), Encoding.UTF8) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); - userInfo.Name.ShouldBe("jimmy"); - } + var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); + userInfo.Name.ShouldBe("jimmy"); } + } - [Fact] - public async Task CanSendStructuredCloudEvent_WithContentType() + [Fact] + public async Task CanSendStructuredCloudEvent_WithContentType() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user") - { - Content = new StringContent( + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user") + { + Content = new StringContent( JsonSerializer.Serialize( new { @@ -99,27 +99,27 @@ public async Task CanSendStructuredCloudEvent_WithContentType() datacontenttype = "text/json", }), Encoding.UTF8) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); - userInfo.Name.ShouldBe("jimmy"); - } + var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); + userInfo.Name.ShouldBe("jimmy"); } + } - [Fact] - public async Task CanSendStructuredCloudEvent_WithNonJsonContentType() + [Fact] + public async Task CanSendStructuredCloudEvent_WithNonJsonContentType() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user-plaintext") - { - Content = new StringContent( + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user-plaintext") + { + Content = new StringContent( JsonSerializer.Serialize( new { @@ -127,45 +127,44 @@ public async Task CanSendStructuredCloudEvent_WithNonJsonContentType() datacontenttype = "text/plain", }), Encoding.UTF8) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/cloudevents+json"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var user = await response.Content.ReadAsStringAsync(); - user.ShouldBe("jimmy \"the cool guy\" smith"); - } + var user = await response.Content.ReadAsStringAsync(); + user.ShouldBe("jimmy \"the cool guy\" smith"); } + } - // Yeah, I know, binary isn't a great term for this, it's what the cloudevents spec uses. - // Basically this is here to test that an endpoint can handle requests with and without - // an envelope. - [Fact] - public async Task CanSendBinaryCloudEvent_WithContentType() + // Yeah, I know, binary isn't a great term for this, it's what the cloudevents spec uses. + // Basically this is here to test that an endpoint can handle requests with and without + // an envelope. + [Fact] + public async Task CanSendBinaryCloudEvent_WithContentType() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user") - { - Content = new StringContent( + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/register-user") + { + Content = new StringContent( JsonSerializer.Serialize( new { name = "jimmy", }), Encoding.UTF8) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); - userInfo.Name.ShouldBe("jimmy"); - } + var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); + userInfo.Name.ShouldBe("jimmy"); } } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs index d3eb87cd6..8bf7316ec 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs @@ -11,157 +11,156 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest +namespace Dapr.AspNetCore.IntegrationTest; + +using System.Net.Http; +using System.Threading.Tasks; +using Dapr.AspNetCore.IntegrationTest.App; +using Shouldly; +using Newtonsoft.Json; +using Xunit; + +public class ControllerIntegrationTest { - using System.Net.Http; - using System.Threading.Tasks; - using Dapr.AspNetCore.IntegrationTest.App; - using Shouldly; - using Newtonsoft.Json; - using Xunit; - - public class ControllerIntegrationTest + [Fact] + public async Task ModelBinder_CanBindFromState() { - [Fact] - public async Task ModelBinder_CanBindFromState() + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var daprClient = factory.DaprClient; - await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); + await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/controllerwithoutstateentry/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/controllerwithoutstateentry/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.ShouldBe(18); - } + var widget = await daprClient.GetStateAsync("testStore", "test"); + widget.Count.ShouldBe(18); } + } - [Fact] - public async Task ModelBinder_GetFromStateEntryWithKeyPresentInStateStore_ReturnsStateValue() + [Fact] + public async Task ModelBinder_GetFromStateEntryWithKeyPresentInStateStore_ReturnsStateValue() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; - - var widget = new Widget() { Size = "small", Count = 17, }; - await daprClient.SaveStateAsync("testStore", "test", widget); - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithoutstateentry/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - var responseWidget = JsonConvert.DeserializeObject(responseContent); - responseWidget.Size.ShouldBe(widget.Size); - responseWidget.Count.ShouldBe(widget.Count); - } + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var daprClient = factory.DaprClient; + + var widget = new Widget() { Size = "small", Count = 17, }; + await daprClient.SaveStateAsync("testStore", "test", widget); + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithoutstateentry/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync(); + var responseWidget = JsonConvert.DeserializeObject(responseContent); + responseWidget.Size.ShouldBe(widget.Size); + responseWidget.Count.ShouldBe(widget.Count); } + } - [Fact] - public async Task ModelBinder_GetFromStateEntryWithKeyNotInStateStore_ReturnsNull() + [Fact] + public async Task ModelBinder_GetFromStateEntryWithKeyNotInStateStore_ReturnsNull() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithoutstateentry/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - var responseWidget = JsonConvert.DeserializeObject(responseContent); - Assert.Null(responseWidget); - } + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithoutstateentry/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync(); + var responseWidget = JsonConvert.DeserializeObject(responseContent); + Assert.Null(responseWidget); } + } - [Fact] - public async Task ModelBinder_CanBindFromState_WithStateEntry() + [Fact] + public async Task ModelBinder_CanBindFromState_WithStateEntry() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var daprClient = factory.DaprClient; - await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); + await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/controllerwithstateentry/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/controllerwithstateentry/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.ShouldBe(18); - } + var widget = await daprClient.GetStateAsync("testStore", "test"); + widget.Count.ShouldBe(18); } + } - [Fact] - public async Task ModelBinder_CanBindFromState_WithStateEntryAndCustomKey() + [Fact] + public async Task ModelBinder_CanBindFromState_WithStateEntryAndCustomKey() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var daprClient = factory.DaprClient; - await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); + await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/controllerwithstateentryandcustomkey/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/controllerwithstateentryandcustomkey/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.ShouldBe(18); - } + var widget = await daprClient.GetStateAsync("testStore", "test"); + widget.Count.ShouldBe(18); } + } - [Fact] - public async Task ModelBinder_GetFromStateEntryWithStateEntry_WithKeyPresentInStateStore() + [Fact] + public async Task ModelBinder_GetFromStateEntryWithStateEntry_WithKeyPresentInStateStore() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; - - var widget = new Widget() { Size = "small", Count = 17, }; - await daprClient.SaveStateAsync("testStore", "test", widget); - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithstateentry/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - var responseWidget = JsonConvert.DeserializeObject(responseContent); - responseWidget.Size.ShouldBe(widget.Size); - responseWidget.Count.ShouldBe(widget.Count); - } + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var daprClient = factory.DaprClient; + + var widget = new Widget() { Size = "small", Count = 17, }; + await daprClient.SaveStateAsync("testStore", "test", widget); + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithstateentry/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync(); + var responseWidget = JsonConvert.DeserializeObject(responseContent); + responseWidget.Size.ShouldBe(widget.Size); + responseWidget.Count.ShouldBe(widget.Count); } + } - [Fact] - public async Task ModelBinder_GetFromStateEntryWithStateEntry_WithKeyNotInStateStore() + [Fact] + public async Task ModelBinder_GetFromStateEntryWithStateEntry_WithKeyNotInStateStore() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithstateentry/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - var responseWidget = JsonConvert.DeserializeObject(responseContent); - Assert.Null(responseWidget); - } + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithstateentry/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync(); + var responseWidget = JsonConvert.DeserializeObject(responseContent); + Assert.Null(responseWidget); } + } - [Fact] - public async Task ModelBinder_CanGetOutOfTheWayWhenTheresNoBinding() + [Fact] + public async Task ModelBinder_CanGetOutOfTheWayWhenTheresNoBinding() + { + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/echo-user?name=jimmy"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - } + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/echo-user?name=jimmy"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); } } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs index 89130c3bd..321302e45 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs @@ -11,33 +11,32 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest -{ - using System.Net.Http; - using System.Threading.Tasks; - using Dapr.AspNetCore.IntegrationTest.App; - using Shouldly; - using Xunit; +namespace Dapr.AspNetCore.IntegrationTest; + +using System.Net.Http; +using System.Threading.Tasks; +using Dapr.AspNetCore.IntegrationTest.App; +using Shouldly; +using Xunit; - public class RoutingIntegrationTest +public class RoutingIntegrationTest +{ + [Fact] + public async Task StateClient_CanBindFromState() { - [Fact] - public async Task StateClient_CanBindFromState() + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var daprClient = factory.DaprClient; - await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); + await daprClient.SaveStateAsync("testStore", "test", new Widget() { Size = "small", Count = 17, }); - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/routingwithstateentry/test"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/routingwithstateentry/test"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.ShouldBe(18); - } + var widget = await daprClient.GetStateAsync("testStore", "test"); + widget.Count.ShouldBe(18); } } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs b/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs index b4940835c..cd18226a2 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs @@ -11,112 +11,111 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Net.Client; +using Autogenerated = Dapr.Client.Autogen.Grpc.v1; + +internal class StateTestClient : DaprClientGrpc { - using System; - using System.Collections.Generic; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Grpc.Net.Client; - using Autogenerated = Dapr.Client.Autogen.Grpc.v1; - - internal class StateTestClient : DaprClientGrpc + public Dictionary State { get; } = new Dictionary(); + private static readonly GrpcChannel channel = GrpcChannel.ForAddress("http://localhost"); + + /// + /// Initializes a new instance of the class. + /// + internal StateTestClient() + : base(channel, new Autogenerated.Dapr.DaprClient(channel), new HttpClient(), new Uri("http://localhost"), null, default) + { + } + + public override Task GetStateAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) { - public Dictionary State { get; } = new Dictionary(); - private static readonly GrpcChannel channel = GrpcChannel.ForAddress("http://localhost"); - - /// - /// Initializes a new instance of the class. - /// - internal StateTestClient() - : base(channel, new Autogenerated.Dapr.DaprClient(channel), new HttpClient(), new Uri("http://localhost"), null, default) + ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); + ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); + + if (this.State.TryGetValue(key, out var obj)) { + return Task.FromResult((TValue)obj); } - - public override Task GetStateAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + else { - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); + return Task.FromResult(default(TValue)); + } + } + public override Task> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); + + var response = new List(); + + foreach (var key in keys) + { if (this.State.TryGetValue(key, out var obj)) { - return Task.FromResult((TValue)obj); + response.Add(new BulkStateItem(key, obj.ToString(), "")); } else { - return Task.FromResult(default(TValue)); + response.Add(new BulkStateItem(key, "", "")); } } - public override Task> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - - var response = new List(); + return Task.FromResult>(response); + } - foreach (var key in keys) - { - if (this.State.TryGetValue(key, out var obj)) - { - response.Add(new BulkStateItem(key, obj.ToString(), "")); - } - else - { - response.Add(new BulkStateItem(key, "", "")); - } - } + public override Task<(TValue value, string etag)> GetStateAndETagAsync( + string storeName, + string key, + ConsistencyMode? consistencyMode = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); + ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); - return Task.FromResult>(response); + if (this.State.TryGetValue(key, out var obj)) + { + return Task.FromResult(((TValue)obj, "test_etag")); } - - public override Task<(TValue value, string etag)> GetStateAndETagAsync( - string storeName, - string key, - ConsistencyMode? consistencyMode = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default) + else { - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); - - if (this.State.TryGetValue(key, out var obj)) - { - return Task.FromResult(((TValue)obj, "test_etag")); - } - else - { - return Task.FromResult((default(TValue), "test_etag")); - } + return Task.FromResult((default(TValue), "test_etag")); } + } - public override Task SaveStateAsync( - string storeName, - string key, - TValue value, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); + public override Task SaveStateAsync( + string storeName, + string key, + TValue value, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); + ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); - this.State[key] = value; - return Task.CompletedTask; - } + this.State[key] = value; + return Task.CompletedTask; + } - public override Task DeleteStateAsync( - string storeName, - string key, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); + public override Task DeleteStateAsync( + string storeName, + string key, + StateOptions stateOptions = default, + IReadOnlyDictionary metadata = default, + CancellationToken cancellationToken = default) + { + ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); + ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); - this.State.Remove(key); - return Task.CompletedTask; - } + this.State.Remove(key); + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs b/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs index 7354f6aa6..e0fdaba6d 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs @@ -11,134 +11,133 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.IntegrationTest -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net.Http; - using System.Text.Json; - using System.Threading.Tasks; - using Shouldly; - using Xunit; +namespace Dapr.AspNetCore.IntegrationTest; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Shouldly; +using Xunit; - public class SubscribeEndpointTest +public class SubscribeEndpointTest +{ + [Fact] + public async Task SubscribeEndpoint_ReportsTopics() { - [Fact] - public async Task SubscribeEndpoint_ReportsTopics() + using (var factory = new AppWebApplicationFactory()) { - using (var factory = new AppWebApplicationFactory()) - { - var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); + var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/dapr/subscribe"); - var response = await httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/dapr/subscribe"); + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); - using (var stream = await response.Content.ReadAsStreamAsync()) - { - var json = await JsonSerializer.DeserializeAsync(stream); + using (var stream = await response.Content.ReadAsStreamAsync()) + { + var json = await JsonSerializer.DeserializeAsync(stream); - json.ValueKind.ShouldBe(JsonValueKind.Array); - json.GetArrayLength().ShouldBe(18); + json.ValueKind.ShouldBe(JsonValueKind.Array); + json.GetArrayLength().ShouldBe(18); - var subscriptions = new List<(string PubsubName, string Topic, string Route, string rawPayload, - string match, string metadata, string DeadLetterTopic, string bulkSubscribeMetadata)>(); + var subscriptions = new List<(string PubsubName, string Topic, string Route, string rawPayload, + string match, string metadata, string DeadLetterTopic, string bulkSubscribeMetadata)>(); - foreach (var element in json.EnumerateArray()) - { - var pubsubName = element.GetProperty("pubsubName").GetString(); - var topic = element.GetProperty("topic").GetString(); - var rawPayload = string.Empty; - var deadLetterTopic = string.Empty; - var bulkSubscribeMetadata = string.Empty; - //JsonElement bulkSubscribeMetadata; - Dictionary originalMetadata = new Dictionary(); + foreach (var element in json.EnumerateArray()) + { + var pubsubName = element.GetProperty("pubsubName").GetString(); + var topic = element.GetProperty("topic").GetString(); + var rawPayload = string.Empty; + var deadLetterTopic = string.Empty; + var bulkSubscribeMetadata = string.Empty; + //JsonElement bulkSubscribeMetadata; + Dictionary originalMetadata = new Dictionary(); - if (element.TryGetProperty("bulkSubscribe", out var BulkSubscribeMetadata)) - { - bulkSubscribeMetadata = BulkSubscribeMetadata.ToString(); - } - if (element.TryGetProperty("deadLetterTopic", out JsonElement DeadLetterTopic)) + if (element.TryGetProperty("bulkSubscribe", out var BulkSubscribeMetadata)) + { + bulkSubscribeMetadata = BulkSubscribeMetadata.ToString(); + } + if (element.TryGetProperty("deadLetterTopic", out JsonElement DeadLetterTopic)) + { + deadLetterTopic = DeadLetterTopic.GetString(); + } + if (element.TryGetProperty("metadata", out JsonElement metadata)) + { + if (metadata.TryGetProperty("rawPayload", out JsonElement rawPayloadJson)) { - deadLetterTopic = DeadLetterTopic.GetString(); + rawPayload = rawPayloadJson.GetString(); } - if (element.TryGetProperty("metadata", out JsonElement metadata)) - { - if (metadata.TryGetProperty("rawPayload", out JsonElement rawPayloadJson)) - { - rawPayload = rawPayloadJson.GetString(); - } - foreach (var originalMetadataProperty in metadata.EnumerateObject().OrderBy(c => c.Name)) + foreach (var originalMetadataProperty in metadata.EnumerateObject().OrderBy(c => c.Name)) + { + if (!originalMetadataProperty.Name.Equals("rawPayload")) { - if (!originalMetadataProperty.Name.Equals("rawPayload")) - { - originalMetadata.Add(originalMetadataProperty.Name, originalMetadataProperty.Value.GetString()); - } + originalMetadata.Add(originalMetadataProperty.Name, originalMetadataProperty.Value.GetString()); } } - var originalMetadataString = string.Empty; - if (originalMetadata.Count > 0) - { - originalMetadataString = string.Join(";", originalMetadata.Select(c => $"{c.Key}={c.Value}")); - } + } + var originalMetadataString = string.Empty; + if (originalMetadata.Count > 0) + { + originalMetadataString = string.Join(";", originalMetadata.Select(c => $"{c.Key}={c.Value}")); + } - if (element.TryGetProperty("route", out JsonElement route)) - { - subscriptions.Add((pubsubName, topic, route.GetString(), rawPayload, string.Empty, - originalMetadataString, deadLetterTopic, bulkSubscribeMetadata)); - } - else if (element.TryGetProperty("routes", out JsonElement routes)) + if (element.TryGetProperty("route", out JsonElement route)) + { + subscriptions.Add((pubsubName, topic, route.GetString(), rawPayload, string.Empty, + originalMetadataString, deadLetterTopic, bulkSubscribeMetadata)); + } + else if (element.TryGetProperty("routes", out JsonElement routes)) + { + if (routes.TryGetProperty("rules", out JsonElement rules)) { - if (routes.TryGetProperty("rules", out JsonElement rules)) - { - foreach (var rule in rules.EnumerateArray()) - { - var match = rule.GetProperty("match").GetString(); - var path = rule.GetProperty("path").GetString(); - subscriptions.Add((pubsubName, topic, path, rawPayload, match, - originalMetadataString, deadLetterTopic, bulkSubscribeMetadata)); - } - } - if (routes.TryGetProperty("default", out JsonElement defaultProperty)) + foreach (var rule in rules.EnumerateArray()) { - subscriptions.Add((pubsubName, topic, defaultProperty.GetString(), rawPayload, - string.Empty, originalMetadataString, deadLetterTopic, bulkSubscribeMetadata)); + var match = rule.GetProperty("match").GetString(); + var path = rule.GetProperty("path").GetString(); + subscriptions.Add((pubsubName, topic, path, rawPayload, match, + originalMetadataString, deadLetterTopic, bulkSubscribeMetadata)); } } + if (routes.TryGetProperty("default", out JsonElement defaultProperty)) + { + subscriptions.Add((pubsubName, topic, defaultProperty.GetString(), rawPayload, + string.Empty, originalMetadataString, deadLetterTopic, bulkSubscribeMetadata)); + } } - - subscriptions.ShouldContain(("testpubsub", "A", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("testpubsub", "A.1", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "B", "B", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("custom-pubsub", "custom-C", "C", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "register-user", "register-user", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "register-user-plaintext", "register-user-plaintext", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "D", "D", "true", string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "E", "E", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "E", "E-Critical", string.Empty, "event.type == \"critical\"", string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "E", "E-Important", string.Empty, "event.type == \"important\"", string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "F", "multiTopicAttr", string.Empty, string.Empty, string.Empty, string.Empty, - "{\"enabled\":true,\"maxMessagesCount\":100,\"maxAwaitDurationMs\":1000}")); - subscriptions.ShouldContain(("pubsub", "F.1", "multiTopicAttr", "true", string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "G", "G", string.Empty, string.Empty, string.Empty, "deadLetterTopicName", - "{\"enabled\":true,\"maxMessagesCount\":300,\"maxAwaitDurationMs\":1000}")); - subscriptions.ShouldContain(("pubsub", "splitTopicBuilder", "splitTopics", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "splitTopicAttr", "splitTopics", "true", string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "metadata", "multiMetadataTopicAttr", string.Empty, string.Empty, "n1=v1;n2=v2,v3", string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "metadata.1", "multiMetadataTopicAttr", "true", string.Empty, "n1=v1", string.Empty, - "{\"enabled\":true,\"maxMessagesCount\":500,\"maxAwaitDurationMs\":2000}")); - subscriptions.ShouldContain(("pubsub", "splitMetadataTopicBuilder", "splitMetadataTopics", string.Empty, string.Empty, "n1=v1;n2=v1", string.Empty, String.Empty)); - subscriptions.ShouldContain(("pubsub", "metadataseparatorbyemptytring", "topicmetadataseparatorattrbyemptytring", string.Empty, string.Empty, "n1=v1,", string.Empty, String.Empty)); - // Test priority route sorting - var eTopic = subscriptions.FindAll(e => e.Topic == "E"); - eTopic.Count.ShouldBe(3); - eTopic[0].Route.ShouldBe("E-Critical"); - eTopic[1].Route.ShouldBe("E-Important"); - eTopic[2].Route.ShouldBe("E"); } + + subscriptions.ShouldContain(("testpubsub", "A", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("testpubsub", "A.1", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "B", "B", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("custom-pubsub", "custom-C", "C", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "register-user", "register-user", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "register-user-plaintext", "register-user-plaintext", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "D", "D", "true", string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "E", "E", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "E", "E-Critical", string.Empty, "event.type == \"critical\"", string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "E", "E-Important", string.Empty, "event.type == \"important\"", string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "F", "multiTopicAttr", string.Empty, string.Empty, string.Empty, string.Empty, + "{\"enabled\":true,\"maxMessagesCount\":100,\"maxAwaitDurationMs\":1000}")); + subscriptions.ShouldContain(("pubsub", "F.1", "multiTopicAttr", "true", string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "G", "G", string.Empty, string.Empty, string.Empty, "deadLetterTopicName", + "{\"enabled\":true,\"maxMessagesCount\":300,\"maxAwaitDurationMs\":1000}")); + subscriptions.ShouldContain(("pubsub", "splitTopicBuilder", "splitTopics", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "splitTopicAttr", "splitTopics", "true", string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "metadata", "multiMetadataTopicAttr", string.Empty, string.Empty, "n1=v1;n2=v2,v3", string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "metadata.1", "multiMetadataTopicAttr", "true", string.Empty, "n1=v1", string.Empty, + "{\"enabled\":true,\"maxMessagesCount\":500,\"maxAwaitDurationMs\":2000}")); + subscriptions.ShouldContain(("pubsub", "splitMetadataTopicBuilder", "splitMetadataTopics", string.Empty, string.Empty, "n1=v1;n2=v1", string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "metadataseparatorbyemptytring", "topicmetadataseparatorattrbyemptytring", string.Empty, string.Empty, "n1=v1,", string.Empty, String.Empty)); + // Test priority route sorting + var eTopic = subscriptions.FindAll(e => e.Topic == "E"); + eTopic.Count.ShouldBe(3); + eTopic[0].Route.ShouldBe("E-Critical"); + eTopic[1].Route.ShouldBe("E-Important"); + eTopic[2].Route.ShouldBe("E"); } } } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs index ae8db689b..54e099be5 100644 --- a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs +++ b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs @@ -13,470 +13,469 @@ using Microsoft.Extensions.DependencyInjection; -namespace Dapr.AspNetCore.Test +namespace Dapr.AspNetCore.Test; + +using System.IO; +using System.Net; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Shouldly; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Xunit; + +public class CloudEventsMiddlewareTest { - using System.IO; - using System.Net; - using System.Text; - using System.Text.Json; - using System.Threading.Tasks; - using Shouldly; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Http; - using Xunit; - - public class CloudEventsMiddlewareTest + [Theory] + [InlineData("text/plain")] + [InlineData("application/json")] // "binary" format + [InlineData("application/cloudevents")] // no format + [InlineData("application/cloudevents+xml")] // wrong format + [InlineData("application/cloudevents-batch+json")] // we don't support batch + public async Task InvokeAsync_IgnoresOtherContentTypes(string contentType) { - [Theory] - [InlineData("text/plain")] - [InlineData("application/json")] // "binary" format - [InlineData("application/cloudevents")] // no format - [InlineData("application/cloudevents+xml")] // wrong format - [InlineData("application/cloudevents-batch+json")] // we don't support batch - public async Task InvokeAsync_IgnoresOtherContentTypes(string contentType) - { - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(); - // Do verification in the scope of the middleware - app.Run(httpContext => - { - httpContext.Request.ContentType.ShouldBe(contentType); - ReadBody(httpContext.Request.Body).ShouldBe("Hello, world!"); - return Task.CompletedTask; - }); + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe(contentType); + ReadBody(httpContext.Request.Body).ShouldBe("Hello, world!"); + return Task.CompletedTask; + }); - var pipeline = app.Build(); + var pipeline = app.Build(); - var context = new DefaultHttpContext - { - Request = { ContentType = contentType, Body = MakeBody("Hello, world!") } - }; - - await pipeline.Invoke(context); - } - - [Theory] - [InlineData(null, null)] // assumes application/json + utf8 - [InlineData("application/json", null)] // assumes utf8 - [InlineData("application/json", "utf-8")] - [InlineData("application/json", "UTF-8")] - [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset - public async Task InvokeAsync_ReplacesBodyJson(string dataContentType, string charSet) + var context = new DefaultHttpContext { - var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + Request = { ContentType = contentType, Body = MakeBody("Hello, world!") } + }; + + await pipeline.Invoke(context); + } + + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_ReplacesBodyJson(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(); + + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); + return Task.CompletedTask; + }); - // Do verification in the scope of the middleware - app.Run(httpContext => + var pipeline = app.Build(); + + var context = new DefaultHttpContext { Request = { - httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - return Task.CompletedTask; - }); - - var pipeline = app.Build(); - - var context = new DefaultHttpContext { Request = - { - ContentType = - charSet == null - ? "application/cloudevents+json" - : $"application/cloudevents+json;charset={charSet}", - Body = dataContentType == null ? - MakeBody("{ \"data\": { \"name\":\"jimmy\" } }", encoding) : - MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) - } - }; - - await pipeline.Invoke(context); - } + ContentType = + charSet == null + ? "application/cloudevents+json" + : $"application/cloudevents+json;charset={charSet}", + Body = dataContentType == null ? + MakeBody("{ \"data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) + } + }; + + await pipeline.Invoke(context); + } - [Theory] - [InlineData(null, null)] // assumes application/json + utf8 - [InlineData("application/json", null)] // assumes utf8 - [InlineData("application/json", "utf-8")] - [InlineData("application/json", "UTF-8")] - [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset - public async Task InvokeAsync_ReplacesPascalCasedBodyJson(string dataContentType, string charSet) - { - var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_ReplacesPascalCasedBodyJson(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(); + + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); - // Do verification in the scope of the middleware - app.Run(httpContext => + var context = new DefaultHttpContext { Request = { - httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - return Task.CompletedTask; - }); - - var pipeline = app.Build(); - - var context = new DefaultHttpContext { Request = - { - ContentType = - charSet == null - ? "application/cloudevents+json" - : $"application/cloudevents+json;charset={charSet}", - Body = dataContentType == null ? - MakeBody("{ \"Data\": { \"name\":\"jimmy\" } }", encoding) : - MakeBody($"{{ \"DataContentType\": \"{dataContentType}\", \"Data\": {{ \"name\":\"jimmy\" }} }}", encoding) - } - }; - - await pipeline.Invoke(context); - } + ContentType = + charSet == null + ? "application/cloudevents+json" + : $"application/cloudevents+json;charset={charSet}", + Body = dataContentType == null ? + MakeBody("{ \"Data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"DataContentType\": \"{dataContentType}\", \"Data\": {{ \"name\":\"jimmy\" }} }}", encoding) + } + }; + + await pipeline.Invoke(context); + } - [Theory] - [InlineData(null, null)] // assumes application/json + utf8 - [InlineData("application/json", null)] // assumes utf8 - [InlineData("application/json", "utf-8")] - [InlineData("application/json", "UTF-8")] - [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset - public async Task InvokeAsync_ForwardsJsonPropertiesAsHeaders(string dataContentType, string charSet) - { - var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_ForwardsJsonPropertiesAsHeaders(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(new CloudEventsMiddlewareOptions - { - ForwardCloudEventPropertiesAsHeaders = true - }); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true + }); + + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); + + httpContext.Request.Headers.ShouldContainKey("Cloudevent.type"); + httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type"); + httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject"); + httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject"); + return Task.CompletedTask; + }); - // Do verification in the scope of the middleware - app.Run(httpContext => + var pipeline = app.Build(); + + var context = new DefaultHttpContext { Request = { - httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - - httpContext.Request.Headers.ShouldContainKey("Cloudevent.type"); - httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type"); - httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject"); - httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject"); - return Task.CompletedTask; - }); - - var pipeline = app.Build(); - - var context = new DefaultHttpContext { Request = - { - ContentType = - charSet == null - ? "application/cloudevents+json" - : $"application/cloudevents+json;charset={charSet}", - Body = dataContentType == null ? - MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : - MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) - } - }; - - await pipeline.Invoke(context); - } + ContentType = + charSet == null + ? "application/cloudevents+json" + : $"application/cloudevents+json;charset={charSet}", + Body = dataContentType == null ? + MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) + } + }; + + await pipeline.Invoke(context); + } - [Theory] - [InlineData(null, null)] // assumes application/json + utf8 - [InlineData("application/json", null)] // assumes utf8 - [InlineData("application/json", "utf-8")] - [InlineData("application/json", "UTF-8")] - [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset - public async Task InvokeAsync_ForwardsIncludedJsonPropertiesAsHeaders(string dataContentType, string charSet) - { - var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_ForwardsIncludedJsonPropertiesAsHeaders(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(new CloudEventsMiddlewareOptions - { - ForwardCloudEventPropertiesAsHeaders = true, - IncludedCloudEventPropertiesAsHeaders = new []{"type"} - }); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true, + IncludedCloudEventPropertiesAsHeaders = new []{"type"} + }); + + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); + + httpContext.Request.Headers.ShouldContainKey("Cloudevent.type"); + httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type"); + httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.subject"); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); - // Do verification in the scope of the middleware - app.Run(httpContext => + var context = new DefaultHttpContext { Request = { - httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - - httpContext.Request.Headers.ShouldContainKey("Cloudevent.type"); - httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type"); - httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.subject"); - return Task.CompletedTask; - }); - - var pipeline = app.Build(); - - var context = new DefaultHttpContext { Request = - { - ContentType = - charSet == null - ? "application/cloudevents+json" - : $"application/cloudevents+json;charset={charSet}", - Body = dataContentType == null ? - MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : - MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) - } - }; - - await pipeline.Invoke(context); - } + ContentType = + charSet == null + ? "application/cloudevents+json" + : $"application/cloudevents+json;charset={charSet}", + Body = dataContentType == null ? + MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) + } + }; + + await pipeline.Invoke(context); + } - [Theory] - [InlineData(null, null)] // assumes application/json + utf8 - [InlineData("application/json", null)] // assumes utf8 - [InlineData("application/json", "utf-8")] - [InlineData("application/json", "UTF-8")] - [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset - public async Task InvokeAsync_DoesNotForwardExcludedJsonPropertiesAsHeaders(string dataContentType, string charSet) - { - var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_DoesNotForwardExcludedJsonPropertiesAsHeaders(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(new CloudEventsMiddlewareOptions - { - ForwardCloudEventPropertiesAsHeaders = true, - ExcludedCloudEventPropertiesFromHeaders = new []{"type"} - }); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true, + ExcludedCloudEventPropertiesFromHeaders = new []{"type"} + }); - // Do verification in the scope of the middleware - app.Run(httpContext => + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); + + httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.type"); + httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject"); + httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject"); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); + + var context = new DefaultHttpContext { Request = { - httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - - httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.type"); - httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject"); - httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject"); - return Task.CompletedTask; - }); - - var pipeline = app.Build(); - - var context = new DefaultHttpContext { Request = - { - ContentType = - charSet == null - ? "application/cloudevents+json" - : $"application/cloudevents+json;charset={charSet}", - Body = dataContentType == null ? - MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : - MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) - } - }; - - await pipeline.Invoke(context); - } + ContentType = + charSet == null + ? "application/cloudevents+json" + : $"application/cloudevents+json;charset={charSet}", + Body = dataContentType == null ? + MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) + } + }; + + await pipeline.Invoke(context); + } - [Fact] - public async Task InvokeAsync_ReplacesBodyNonJsonData() + [Fact] + public async Task InvokeAsync_ReplacesBodyNonJsonData() + { + // Our logic is based on the content-type, not the content. + // Since this is for text-plain content, we're going to encode it as a JSON string + // and store it in the data attribute - the middleware should JSON-decode it. + const string input = "{ \"message\": \"hello, world\"}"; + var expected = input; + + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); + + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(); + + // Do verification in the scope of the middleware + app.Run(httpContext => { - // Our logic is based on the content-type, not the content. - // Since this is for text-plain content, we're going to encode it as a JSON string - // and store it in the data attribute - the middleware should JSON-decode it. - const string input = "{ \"message\": \"hello, world\"}"; - var expected = input; - - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + httpContext.Request.ContentType.ShouldBe("text/plain"); + ReadBody(httpContext.Request.Body).ShouldBe(expected); + return Task.CompletedTask; + }); + - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(); + var pipeline = app.Build(); - // Do verification in the scope of the middleware - app.Run(httpContext => + var context = new DefaultHttpContext { Request = { - httpContext.Request.ContentType.ShouldBe("text/plain"); - ReadBody(httpContext.Request.Body).ShouldBe(expected); - return Task.CompletedTask; - }); + ContentType = "application/cloudevents+json", + Body = MakeBody($"{{ \"datacontenttype\": \"text/plain\", \"data\": {JsonSerializer.Serialize(input)} }}") + } + }; - - var pipeline = app.Build(); + await pipeline.Invoke(context); + } - var context = new DefaultHttpContext { Request = - { - ContentType = "application/cloudevents+json", - Body = MakeBody($"{{ \"datacontenttype\": \"text/plain\", \"data\": {JsonSerializer.Serialize(input)} }}") - } - }; + [Fact] + public async Task InvokeAsync_ReplacesBodyNonJsonData_ExceptWhenSuppressed() + { + // Our logic is based on the content-type, not the content. This test tests the old bad behavior. + const string input = "{ \"message\": \"hello, world\"}"; + var expected = JsonSerializer.Serialize(input); - await pipeline.Invoke(context); - } + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); + + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(new CloudEventsMiddlewareOptions() { SuppressJsonDecodingOfTextPayloads = true, }); - [Fact] - public async Task InvokeAsync_ReplacesBodyNonJsonData_ExceptWhenSuppressed() + // Do verification in the scope of the middleware + app.Run(httpContext => { - // Our logic is based on the content-type, not the content. This test tests the old bad behavior. - const string input = "{ \"message\": \"hello, world\"}"; - var expected = JsonSerializer.Serialize(input); + httpContext.Request.ContentType.ShouldBe("text/plain"); + ReadBody(httpContext.Request.Body).ShouldBe(expected); + return Task.CompletedTask; + }); - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(new CloudEventsMiddlewareOptions() { SuppressJsonDecodingOfTextPayloads = true, }); + var pipeline = app.Build(); - // Do verification in the scope of the middleware - app.Run(httpContext => + var context = new DefaultHttpContext { Request = { - httpContext.Request.ContentType.ShouldBe("text/plain"); - ReadBody(httpContext.Request.Body).ShouldBe(expected); - return Task.CompletedTask; - }); + ContentType = "application/cloudevents+json", + Body = MakeBody($"{{ \"datacontenttype\": \"text/plain\", \"data\": {JsonSerializer.Serialize(input)} }}") + } + }; + await pipeline.Invoke(context); + } + + // This is a special case. S.T.Json will always output utf8, so we have to reinterpret the charset + // of the datacontenttype. + [Fact] + public async Task InvokeAsync_ReplacesBodyJson_NormalizesPayloadCharset() + { + const string dataContentType = "application/person+json;charset=UTF-16"; + const string charSet = "UTF-16"; + var encoding = Encoding.GetEncoding(charSet); + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var pipeline = app.Build(); - - var context = new DefaultHttpContext { Request = - { - ContentType = "application/cloudevents+json", - Body = MakeBody($"{{ \"datacontenttype\": \"text/plain\", \"data\": {JsonSerializer.Serialize(input)} }}") - } - }; - - await pipeline.Invoke(context); - } - - // This is a special case. S.T.Json will always output utf8, so we have to reinterpret the charset - // of the datacontenttype. - [Fact] - public async Task InvokeAsync_ReplacesBodyJson_NormalizesPayloadCharset() - { - const string dataContentType = "application/person+json;charset=UTF-16"; - const string charSet = "UTF-16"; - var encoding = Encoding.GetEncoding(charSet); - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); - - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(); - // Do verification in the scope of the middleware - app.Run(httpContext => - { - httpContext.Request.ContentType.ShouldBe("application/person+json"); - ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - return Task.CompletedTask; - }); + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe("application/person+json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); + return Task.CompletedTask; + }); - var pipeline = app.Build(); + var pipeline = app.Build(); - var context = new DefaultHttpContext { Request = - { - ContentType = $"application/cloudevents+json;charset={charSet}", Body = MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) - } - }; + var context = new DefaultHttpContext { Request = + { + ContentType = $"application/cloudevents+json;charset={charSet}", Body = MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding) + } + }; - await pipeline.Invoke(context); - } + await pipeline.Invoke(context); + } - [Fact] - public async Task InvokeAsync_ReadsBinaryData() - { - const string dataContentType = "application/octet-stream"; - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + [Fact] + public async Task InvokeAsync_ReadsBinaryData() + { + const string dataContentType = "application/octet-stream"; + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(); - var data = new byte[] { 1, 2, 3 }; + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(); + var data = new byte[] { 1, 2, 3 }; - // Do verification in the scope of the middleware - app.Run(httpContext => - { - httpContext.Request.ContentType.ShouldBe(dataContentType); - var bytes = new byte[httpContext.Request.Body.Length]; + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.ShouldBe(dataContentType); + var bytes = new byte[httpContext.Request.Body.Length]; #if NET9_0 - httpContext.Request.Body.ReadExactly(bytes, 0, bytes.Length); + httpContext.Request.Body.ReadExactly(bytes, 0, bytes.Length); #else httpContext.Request.Body.Read(bytes, 0, bytes.Length); #endif - bytes.ShouldBe(data); - return Task.CompletedTask; - }); + bytes.ShouldBe(data); + return Task.CompletedTask; + }); - var pipeline = app.Build(); + var pipeline = app.Build(); - var context = new DefaultHttpContext { Request = { ContentType = "application/cloudevents+json" } }; - var base64Str = System.Convert.ToBase64String(data); + var context = new DefaultHttpContext { Request = { ContentType = "application/cloudevents+json" } }; + var base64Str = System.Convert.ToBase64String(data); - context.Request.Body = - MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\"}}"); + context.Request.Body = + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\"}}"); - await pipeline.Invoke(context); - } + await pipeline.Invoke(context); + } - [Fact] - public async Task InvokeAsync_DataAndData64Set_ReturnsBadRequest() - { - const string dataContentType = "application/octet-stream"; - var serviceCollection = new ServiceCollection(); - var provider = serviceCollection.BuildServiceProvider(); + [Fact] + public async Task InvokeAsync_DataAndData64Set_ReturnsBadRequest() + { + const string dataContentType = "application/octet-stream"; + var serviceCollection = new ServiceCollection(); + var provider = serviceCollection.BuildServiceProvider(); - var app = new ApplicationBuilder(provider); - app.UseCloudEvents(); - const string data = "{\"id\": \"1\"}"; - - // Do verification in the scope of the middleware - app.Run(httpContext => - { - httpContext.Request.ContentType.ShouldBe("application/json"); - var body = ReadBody(httpContext.Request.Body); - body.ShouldBe(data); - return Task.CompletedTask; - }); - - var pipeline = app.Build(); - - var context = new DefaultHttpContext { Request = { ContentType = "application/cloudevents+json" } }; - var bytes = Encoding.UTF8.GetBytes(data); - var base64Str = System.Convert.ToBase64String(bytes); - context.Request.Body = - MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\", \"data\": {data} }}"); + var app = new ApplicationBuilder(provider); + app.UseCloudEvents(); + const string data = "{\"id\": \"1\"}"; - await pipeline.Invoke(context); - context.Response.StatusCode.ShouldBe((int)HttpStatusCode.BadRequest); - } - - private static Stream MakeBody(string text, Encoding encoding = null) + // Do verification in the scope of the middleware + app.Run(httpContext => { - encoding ??= Encoding.UTF8; + httpContext.Request.ContentType.ShouldBe("application/json"); + var body = ReadBody(httpContext.Request.Body); + body.ShouldBe(data); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); + + var context = new DefaultHttpContext { Request = { ContentType = "application/cloudevents+json" } }; + var bytes = Encoding.UTF8.GetBytes(data); + var base64Str = System.Convert.ToBase64String(bytes); + context.Request.Body = + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\", \"data\": {data} }}"); + + await pipeline.Invoke(context); + context.Response.StatusCode.ShouldBe((int)HttpStatusCode.BadRequest); + } - var stream = new MemoryStream(); - var bytes = encoding.GetBytes(text); - stream.Write(bytes); - stream.Seek(0L, SeekOrigin.Begin); - return stream; - } + private static Stream MakeBody(string text, Encoding encoding = null) + { + encoding ??= Encoding.UTF8; - private static string ReadBody(Stream stream, Encoding encoding = null) - { - encoding ??= Encoding.UTF8; + var stream = new MemoryStream(); + var bytes = encoding.GetBytes(text); + stream.Write(bytes); + stream.Seek(0L, SeekOrigin.Begin); + return stream; + } + + private static string ReadBody(Stream stream, Encoding encoding = null) + { + encoding ??= Encoding.UTF8; - var bytes = new byte[stream.Length]; + var bytes = new byte[stream.Length]; #if NET9_0 - stream.ReadExactly(bytes, 0, bytes.Length); + stream.ReadExactly(bytes, 0, bytes.Length); #else stream.Read(bytes, 0, bytes.Length); #endif - var str = encoding.GetString(bytes); - return str; - } + var str = encoding.GetString(bytes); + return str; } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs index 45cbcc152..22f62fcfd 100644 --- a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs @@ -17,108 +17,106 @@ using Grpc.Net.Client; using Xunit; -namespace Dapr.AspNetCore.Test +namespace Dapr.AspNetCore.Test; + +public class DaprClientBuilderTest { - public class DaprClientBuilderTest + [Fact] + public void DaprClientBuilder_UsesPropertyNameCaseHandlingInsensitiveByDefault() { - [Fact] - public void DaprClientBuilder_UsesPropertyNameCaseHandlingInsensitiveByDefault() - { - DaprClientBuilder builder = new DaprClientBuilder(); - Assert.True(builder.JsonSerializerOptions.PropertyNameCaseInsensitive); - } + DaprClientBuilder builder = new DaprClientBuilder(); + Assert.True(builder.JsonSerializerOptions.PropertyNameCaseInsensitive); + } - [Fact] - public void DaprClientBuilder_UsesPropertyNameCaseHandlingAsSpecified() + [Fact] + public void DaprClientBuilder_UsesPropertyNameCaseHandlingAsSpecified() + { + var builder = new DaprClientBuilder(); + builder.UseJsonSerializationOptions(new JsonSerializerOptions { - var builder = new DaprClientBuilder(); - builder.UseJsonSerializationOptions(new JsonSerializerOptions - { - PropertyNameCaseInsensitive = false - }); - Assert.False(builder.JsonSerializerOptions.PropertyNameCaseInsensitive); - } + PropertyNameCaseInsensitive = false + }); + Assert.False(builder.JsonSerializerOptions.PropertyNameCaseInsensitive); + } - [Fact] - public void DaprClientBuilder_UsesThrowOperationCanceledOnCancellation_ByDefault() - { - var builder = new DaprClientBuilder(); + [Fact] + public void DaprClientBuilder_UsesThrowOperationCanceledOnCancellation_ByDefault() + { + var builder = new DaprClientBuilder(); - Assert.True(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); - } + Assert.True(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); + } - [Fact] - public void DaprClientBuilder_DoesNotOverrideUserGrpcChannelOptions() - { - var builder = new DaprClientBuilder(); - builder.UseGrpcChannelOptions(new GrpcChannelOptions()).Build(); - Assert.False(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); - } + [Fact] + public void DaprClientBuilder_DoesNotOverrideUserGrpcChannelOptions() + { + var builder = new DaprClientBuilder(); + builder.UseGrpcChannelOptions(new GrpcChannelOptions()).Build(); + Assert.False(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); + } - [Fact] - public void DaprClientBuilder_ValidatesGrpcEndpointScheme() - { - var builder = new DaprClientBuilder(); - builder.UseGrpcEndpoint("ftp://example.com"); + [Fact] + public void DaprClientBuilder_ValidatesGrpcEndpointScheme() + { + var builder = new DaprClientBuilder(); + builder.UseGrpcEndpoint("ftp://example.com"); - var ex = Assert.Throws(() => builder.Build()); - Assert.Equal("The gRPC endpoint must use http or https.", ex.Message); - } + var ex = Assert.Throws(() => builder.Build()); + Assert.Equal("The gRPC endpoint must use http or https.", ex.Message); + } - [Fact] - public void DaprClientBuilder_ValidatesHttpEndpointScheme() - { - var builder = new DaprClientBuilder(); - builder.UseHttpEndpoint("ftp://example.com"); + [Fact] + public void DaprClientBuilder_ValidatesHttpEndpointScheme() + { + var builder = new DaprClientBuilder(); + builder.UseHttpEndpoint("ftp://example.com"); - var ex = Assert.Throws(() => builder.Build()); - Assert.Equal("The HTTP endpoint must use http or https.", ex.Message); - } - - [Fact] - public void DaprClientBuilder_SetsApiToken() - { - var builder = new DaprClientBuilder(); - builder.UseDaprApiToken("test_token"); - builder.Build(); - Assert.Equal("test_token", builder.DaprApiToken); - } + var ex = Assert.Throws(() => builder.Build()); + Assert.Equal("The HTTP endpoint must use http or https.", ex.Message); + } - [Fact] - public void DaprClientBuilder_SetsNullApiToken() - { - var builder = new DaprClientBuilder(); - builder.UseDaprApiToken(null); - builder.Build(); - Assert.Null(builder.DaprApiToken); - } + [Fact] + public void DaprClientBuilder_SetsApiToken() + { + var builder = new DaprClientBuilder(); + builder.UseDaprApiToken("test_token"); + builder.Build(); + Assert.Equal("test_token", builder.DaprApiToken); + } - [Fact] - public void DaprClientBuilder_ApiTokenSet_SetsApiTokenHeader() - { - var builder = new DaprClientBuilder(); - builder.UseDaprApiToken("test_token"); - var entry = DaprClient.GetDaprApiTokenHeader(builder.DaprApiToken); - Assert.NotNull(entry); - Assert.Equal("test_token", entry.Value.Value); - } + [Fact] + public void DaprClientBuilder_SetsNullApiToken() + { + var builder = new DaprClientBuilder(); + builder.UseDaprApiToken(null); + builder.Build(); + Assert.Null(builder.DaprApiToken); + } - [Fact] - public void DaprClientBuilder_ApiTokenNotSet_EmptyApiTokenHeader() - { - var builder = new DaprClientBuilder(); - var entry = DaprClient.GetDaprApiTokenHeader(builder.DaprApiToken); - Assert.Equal(default, entry); - } + [Fact] + public void DaprClientBuilder_ApiTokenSet_SetsApiTokenHeader() + { + var builder = new DaprClientBuilder(); + builder.UseDaprApiToken("test_token"); + var entry = DaprClient.GetDaprApiTokenHeader(builder.DaprApiToken); + Assert.NotNull(entry); + Assert.Equal("test_token", entry.Value.Value); + } - [Fact] - public void DaprClientBuilder_SetsTimeout() - { - var builder = new DaprClientBuilder(); - builder.UseTimeout(TimeSpan.FromSeconds(2)); - builder.Build(); - Assert.Equal(2, builder.Timeout.Seconds); - } + [Fact] + public void DaprClientBuilder_ApiTokenNotSet_EmptyApiTokenHeader() + { + var builder = new DaprClientBuilder(); + var entry = DaprClient.GetDaprApiTokenHeader(builder.DaprApiToken); + Assert.Equal(default, entry); } -} + [Fact] + public void DaprClientBuilder_SetsTimeout() + { + var builder = new DaprClientBuilder(); + builder.UseTimeout(TimeSpan.FromSeconds(2)); + builder.Build(); + Assert.Equal(2, builder.Timeout.Seconds); + } +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.Test/DaprMvcBuilderExtensionsTest.cs b/test/Dapr.AspNetCore.Test/DaprMvcBuilderExtensionsTest.cs index a1df1fe1e..e72d9e4e8 100644 --- a/test/Dapr.AspNetCore.Test/DaprMvcBuilderExtensionsTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprMvcBuilderExtensionsTest.cs @@ -17,90 +17,89 @@ using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Dapr.AspNetCore.Test +namespace Dapr.AspNetCore.Test; + +public class DaprMvcBuilderExtensionsTest { - public class DaprMvcBuilderExtensionsTest + [Fact] + public void AddDapr_UsesSpecifiedDaprClientBuilderConfig() { - [Fact] - public void AddDapr_UsesSpecifiedDaprClientBuilderConfig() - { - var services = new ServiceCollection(); + var services = new ServiceCollection(); - var clientBuilder = new Action( - builder => builder.UseJsonSerializationOptions( - new JsonSerializerOptions() - { - PropertyNameCaseInsensitive = false - } - ) - ); + var clientBuilder = new Action( + builder => builder.UseJsonSerializationOptions( + new JsonSerializerOptions() + { + PropertyNameCaseInsensitive = false + } + ) + ); - services.AddControllers().AddDapr(clientBuilder); + services.AddControllers().AddDapr(clientBuilder); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; + DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; - Assert.False(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); - } + Assert.False(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); + } - [Fact] - public void AddDapr_UsesDefaultDaprClientBuilderConfig() - { - var services = new ServiceCollection(); + [Fact] + public void AddDapr_UsesDefaultDaprClientBuilderConfig() + { + var services = new ServiceCollection(); - services.AddControllers().AddDapr(); + services.AddControllers().AddDapr(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; + DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; - Assert.True(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); - } + Assert.True(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); + } - [Fact] - public void AddDapr_RegistersDaprOnlyOnce() - { - var services = new ServiceCollection(); + [Fact] + public void AddDapr_RegistersDaprOnlyOnce() + { + var services = new ServiceCollection(); - var clientBuilder = new Action( - builder => builder.UseJsonSerializationOptions( - new JsonSerializerOptions() - { - PropertyNameCaseInsensitive = false - } - ) - ); + var clientBuilder = new Action( + builder => builder.UseJsonSerializationOptions( + new JsonSerializerOptions() + { + PropertyNameCaseInsensitive = false + } + ) + ); - // register with JsonSerializerOptions.PropertyNameCaseInsensitive = false - services.AddControllers().AddDapr(clientBuilder); + // register with JsonSerializerOptions.PropertyNameCaseInsensitive = false + services.AddControllers().AddDapr(clientBuilder); - // register with JsonSerializerOptions.PropertyNameCaseInsensitive = true (default) - services.AddControllers().AddDapr(); + // register with JsonSerializerOptions.PropertyNameCaseInsensitive = true (default) + services.AddControllers().AddDapr(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; + DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; - Assert.False(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); - } + Assert.False(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); + } #if NET8_0_OR_GREATER - [Fact] - public void AddDapr_WithKeyedServices() - { - var services = new ServiceCollection(); + [Fact] + public void AddDapr_WithKeyedServices() + { + var services = new ServiceCollection(); - services.AddKeyedSingleton("key1", new Object()); + services.AddKeyedSingleton("key1", new Object()); - services.AddControllers().AddDapr(); + services.AddControllers().AddDapr(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var daprClient = serviceProvider.GetService(); + var daprClient = serviceProvider.GetService(); - Assert.NotNull(daprClient); - } -#endif + Assert.NotNull(daprClient); } -} +#endif +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs b/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs index 243b60bb6..06055cb00 100644 --- a/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs +++ b/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs @@ -11,81 +11,80 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.Test +namespace Dapr.AspNetCore.Test; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Dapr.AspNetCore.Resources; +using Shouldly; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Xunit; + +public class StateEntryApplicationModelProviderTest { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using Dapr.AspNetCore.Resources; - using Shouldly; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.ApplicationModels; - using Microsoft.AspNetCore.Mvc.ModelBinding; - using Xunit; - - public class StateEntryApplicationModelProviderTest + [Fact] + public void OnProvidersExecuted_NullActionsBindingSource() { - [Fact] - public void OnProvidersExecuted_NullActionsBindingSource() - { - var provider = new StateEntryApplicationModelProvider(); - var context = CreateContext(nameof(ApplicationModelProviderTestController.Get)); + var provider = new StateEntryApplicationModelProvider(); + var context = CreateContext(nameof(ApplicationModelProviderTestController.Get)); - Action action = () => provider.OnProvidersExecuted(context); + Action action = () => provider.OnProvidersExecuted(context); - action.ShouldNotThrow(); - } + action.ShouldNotThrow(); + } - [Fact] - public void OnProvidersExecuted_StateEntryParameterThrows() - { - var provider = new StateEntryApplicationModelProvider(); - var context = CreateContext(nameof(ApplicationModelProviderTestController.Post)); + [Fact] + public void OnProvidersExecuted_StateEntryParameterThrows() + { + var provider = new StateEntryApplicationModelProvider(); + var context = CreateContext(nameof(ApplicationModelProviderTestController.Post)); - Action action = () => provider.OnProvidersExecuted(context); + Action action = () => provider.OnProvidersExecuted(context); - action - .ShouldThrow(SR.ErrorStateStoreNameNotProvidedForStateEntry); - } + action + .ShouldThrow(SR.ErrorStateStoreNameNotProvidedForStateEntry); + } - private ApplicationModelProviderContext CreateContext(string methodName) - { - var controllerType = typeof(ApplicationModelProviderTestController).GetTypeInfo(); - var typeInfoList = new List { controllerType }; + private ApplicationModelProviderContext CreateContext(string methodName) + { + var controllerType = typeof(ApplicationModelProviderTestController).GetTypeInfo(); + var typeInfoList = new List { controllerType }; - var context = new ApplicationModelProviderContext(typeInfoList); - var controllerModel = new ControllerModel(controllerType, new List(0)); + var context = new ApplicationModelProviderContext(typeInfoList); + var controllerModel = new ControllerModel(controllerType, new List(0)); - context.Result.Controllers.Add(controllerModel); + context.Result.Controllers.Add(controllerModel); - var methodInfo = controllerType.AsType().GetMethods().First(m => m.Name.Equals(methodName)); - var actionModel = new ActionModel(methodInfo, controllerModel.Attributes) - { - Controller = controllerModel - }; + var methodInfo = controllerType.AsType().GetMethods().First(m => m.Name.Equals(methodName)); + var actionModel = new ActionModel(methodInfo, controllerModel.Attributes) + { + Controller = controllerModel + }; - controllerModel.Actions.Add(actionModel); - var parameterInfo = actionModel.ActionMethod.GetParameters().First(); - var parameterModel = new ParameterModel(parameterInfo, controllerModel.Attributes) - { - BindingInfo = new BindingInfo(), - Action = actionModel, - }; + controllerModel.Actions.Add(actionModel); + var parameterInfo = actionModel.ActionMethod.GetParameters().First(); + var parameterModel = new ParameterModel(parameterInfo, controllerModel.Attributes) + { + BindingInfo = new BindingInfo(), + Action = actionModel, + }; - actionModel.Parameters.Add(parameterModel); + actionModel.Parameters.Add(parameterModel); - return context; - } + return context; + } - [Controller] - private class ApplicationModelProviderTestController : Controller - { - [HttpGet] - public void Get([Bind(Prefix = "s")]int someId) { } + [Controller] + private class ApplicationModelProviderTestController : Controller + { + [HttpGet] + public void Get([Bind(Prefix = "s")]int someId) { } - [HttpPost] - public void Post(StateEntry bogusEntry) { } - } + [HttpPost] + public void Post(StateEntry bogusEntry) { } } -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs b/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs index 8cdcd32dd..85170d827 100644 --- a/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs +++ b/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs @@ -11,186 +11,185 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.AspNetCore.Test +namespace Dapr.AspNetCore.Test; + +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Dapr.Client; +using Dapr.Client.Autogen.Grpc.v1; +using Shouldly; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +public class StateEntryModelBinderTest { - using System; - using System.Text.Json; - using System.Threading.Tasks; - using Dapr.Client; - using Dapr.Client.Autogen.Grpc.v1; - using Shouldly; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.ModelBinding; - using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; - using Microsoft.Extensions.DependencyInjection; - using Xunit; - - public class StateEntryModelBinderTest + [Fact] + public async Task BindAsync_WithoutMatchingRouteValue_ReportsError() { - [Fact] - public async Task BindAsync_WithoutMatchingRouteValue_ReportsError() - { - await using var client = TestClient.CreateForDaprClient(); + await using var client = TestClient.CreateForDaprClient(); - var binder = new StateEntryModelBinder("testStore", "test", isStateEntry: false, typeof(Widget)); - var context = CreateContext(CreateServices(client.InnerClient)); + var binder = new StateEntryModelBinder("testStore", "test", isStateEntry: false, typeof(Widget)); + var context = CreateContext(CreateServices(client.InnerClient)); - await binder.BindModelAsync(context); + await binder.BindModelAsync(context); - context.Result.IsModelSet.ShouldBeFalse(); - context.ModelState.ErrorCount.ShouldBe(1); - context.ModelState["testParameter"].Errors.Count.ShouldBe(1); + context.Result.IsModelSet.ShouldBeFalse(); + context.ModelState.ErrorCount.ShouldBe(1); + context.ModelState["testParameter"].Errors.Count.ShouldBe(1); - // No request to state store, validated by disposing client - } + // No request to state store, validated by disposing client + } + + [Fact] + public async Task BindAsync_CanBindValue() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task BindAsync_CanBindValue() + var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: false, typeof(Widget)); + + // Configure Client + var context = CreateContext(CreateServices(client.InnerClient)); + context.HttpContext.Request.RouteValues["id"] = "test"; + + var request = await client.CaptureGrpcRequestAsync(async _ => { - await using var client = TestClient.CreateForDaprClient(); + await binder.BindModelAsync(context); + }); - var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: false, typeof(Widget)); + // Create Response & Respond + var state = new Widget() { Size = "small", Color = "yellow", }; + await SendResponseWithState(state, request); - // Configure Client - var context = CreateContext(CreateServices(client.InnerClient)); - context.HttpContext.Request.RouteValues["id"] = "test"; + // Get response and validate + context.Result.IsModelSet.ShouldBeTrue(); + ((Widget)context.Result.Model).Size.ShouldBe("small"); + ((Widget)context.Result.Model).Color.ShouldBe("yellow"); - var request = await client.CaptureGrpcRequestAsync(async _ => - { - await binder.BindModelAsync(context); - }); + context.ValidationState.Count.ShouldBe(1); + context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue(); + } - // Create Response & Respond - var state = new Widget() { Size = "small", Color = "yellow", }; - await SendResponseWithState(state, request); + [Fact] + public async Task BindAsync_CanBindStateEntry() + { + await using var client = TestClient.CreateForDaprClient(); - // Get response and validate - context.Result.IsModelSet.ShouldBeTrue(); - ((Widget)context.Result.Model).Size.ShouldBe("small"); - ((Widget)context.Result.Model).Color.ShouldBe("yellow"); + var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: true, typeof(Widget)); - context.ValidationState.Count.ShouldBe(1); - context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue(); - } + // Configure Client + var context = CreateContext(CreateServices(client.InnerClient)); + context.HttpContext.Request.RouteValues["id"] = "test"; - [Fact] - public async Task BindAsync_CanBindStateEntry() + var request = await client.CaptureGrpcRequestAsync(async _ => { - await using var client = TestClient.CreateForDaprClient(); + await binder.BindModelAsync(context); + }); - var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: true, typeof(Widget)); + // Create Response & Respond + var state = new Widget() { Size = "small", Color = "yellow", }; + await SendResponseWithState(state, request); - // Configure Client - var context = CreateContext(CreateServices(client.InnerClient)); - context.HttpContext.Request.RouteValues["id"] = "test"; + // Get response and validate + context.Result.IsModelSet.ShouldBeTrue(); + ((StateEntry)context.Result.Model).Key.ShouldBe("test"); + ((StateEntry)context.Result.Model).Value.Size.ShouldBe("small"); + ((StateEntry)context.Result.Model).Value.Color.ShouldBe("yellow"); - var request = await client.CaptureGrpcRequestAsync(async _ => - { - await binder.BindModelAsync(context); - }); + context.ValidationState.Count.ShouldBe(1); + context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue(); + } - // Create Response & Respond - var state = new Widget() { Size = "small", Color = "yellow", }; - await SendResponseWithState(state, request); + [Fact] + public async Task BindAsync_ReturnsNullForNonExistentStateEntry() + { + await using var client = TestClient.CreateForDaprClient(); - // Get response and validate - context.Result.IsModelSet.ShouldBeTrue(); - ((StateEntry)context.Result.Model).Key.ShouldBe("test"); - ((StateEntry)context.Result.Model).Value.Size.ShouldBe("small"); - ((StateEntry)context.Result.Model).Value.Color.ShouldBe("yellow"); + var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: false, typeof(Widget)); - context.ValidationState.Count.ShouldBe(1); - context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue(); - } + // Configure Client + var context = CreateContext(CreateServices(client.InnerClient)); + context.HttpContext.Request.RouteValues["id"] = "test"; - [Fact] - public async Task BindAsync_ReturnsNullForNonExistentStateEntry() + var request = await client.CaptureGrpcRequestAsync(async _ => { - await using var client = TestClient.CreateForDaprClient(); + await binder.BindModelAsync(context); + }); - var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: false, typeof(Widget)); + await SendResponseWithState(null, request); - // Configure Client - var context = CreateContext(CreateServices(client.InnerClient)); - context.HttpContext.Request.RouteValues["id"] = "test"; + context.ModelState.IsValid.ShouldBeTrue(); + context.Result.IsModelSet.ShouldBeFalse(); + context.Result.ShouldBe(ModelBindingResult.Failed()); + } - var request = await client.CaptureGrpcRequestAsync(async _ => - { - await binder.BindModelAsync(context); - }); + [Fact] + public async Task BindAsync_WithStateEntry_ForNonExistentStateEntry() + { + await using var client = TestClient.CreateForDaprClient(); - await SendResponseWithState(null, request); + var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: true, typeof(Widget)); - context.ModelState.IsValid.ShouldBeTrue(); - context.Result.IsModelSet.ShouldBeFalse(); - context.Result.ShouldBe(ModelBindingResult.Failed()); - } + // Configure Client + var context = CreateContext(CreateServices(client.InnerClient)); + context.HttpContext.Request.RouteValues["id"] = "test"; - [Fact] - public async Task BindAsync_WithStateEntry_ForNonExistentStateEntry() + var request = await client.CaptureGrpcRequestAsync(async _ => { - await using var client = TestClient.CreateForDaprClient(); - - var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: true, typeof(Widget)); - - // Configure Client - var context = CreateContext(CreateServices(client.InnerClient)); - context.HttpContext.Request.RouteValues["id"] = "test"; - - var request = await client.CaptureGrpcRequestAsync(async _ => - { - await binder.BindModelAsync(context); - }); + await binder.BindModelAsync(context); + }); - await SendResponseWithState(null, request); + await SendResponseWithState(null, request); - context.ModelState.IsValid.ShouldBeTrue(); - context.Result.IsModelSet.ShouldBeTrue(); - ((StateEntry)context.Result.Model).Value.ShouldBeNull(); - } + context.ModelState.IsValid.ShouldBeTrue(); + context.Result.IsModelSet.ShouldBeTrue(); + ((StateEntry)context.Result.Model).Value.ShouldBeNull(); + } - private static ModelBindingContext CreateContext(IServiceProvider services) + private static ModelBindingContext CreateContext(IServiceProvider services) + { + return new DefaultModelBindingContext() { - return new DefaultModelBindingContext() + ActionContext = new ActionContext() { - ActionContext = new ActionContext() + HttpContext = new DefaultHttpContext() { - HttpContext = new DefaultHttpContext() - { - RequestServices = services, - }, + RequestServices = services, }, - ModelState = new ModelStateDictionary(), - ModelName = "testParameter", - ValidationState = new ValidationStateDictionary(), - }; - } + }, + ModelState = new ModelStateDictionary(), + ModelName = "testParameter", + ValidationState = new ValidationStateDictionary(), + }; + } - private async Task SendResponseWithState(T state, TestClient.TestGrpcRequest request) + private async Task SendResponseWithState(T state, TestClient.TestGrpcRequest request) + { + var stateData = TypeConverters.ToJsonByteString(state, new JsonSerializerOptions(JsonSerializerDefaults.Web)); + var stateResponse = new GetStateResponse() { - var stateData = TypeConverters.ToJsonByteString(state, new JsonSerializerOptions(JsonSerializerDefaults.Web)); - var stateResponse = new GetStateResponse() - { - Data = stateData, - Etag = "test", - }; + Data = stateData, + Etag = "test", + }; - await request.CompleteWithMessageAsync(stateResponse); - } + await request.CompleteWithMessageAsync(stateResponse); + } - private static IServiceProvider CreateServices(DaprClient daprClient) - { - var services = new ServiceCollection(); - services.AddSingleton(daprClient); - return services.BuildServiceProvider(); - } + private static IServiceProvider CreateServices(DaprClient daprClient) + { + var services = new ServiceCollection(); + services.AddSingleton(daprClient); + return services.BuildServiceProvider(); + } - private class Widget - { - public string Size { get; set; } + private class Widget + { + public string Size { get; set; } - public string Color { get; set; } - } + public string Color { get; set; } } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/ArgumentVerifierTest.cs b/test/Dapr.Client.Test/ArgumentVerifierTest.cs index c839ac3eb..9b386b0f8 100644 --- a/test/Dapr.Client.Test/ArgumentVerifierTest.cs +++ b/test/Dapr.Client.Test/ArgumentVerifierTest.cs @@ -11,44 +11,43 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client.Test -{ - using System; - using Xunit; +namespace Dapr.Client.Test; + +using System; +using Xunit; - public class ArgumentVerifierTest +public class ArgumentVerifierTest +{ + [Fact] + public void ThrowIfNull_RespectsArgumentName() { - [Fact] - public void ThrowIfNull_RespectsArgumentName() + var ex = Assert.Throws(() => { - var ex = Assert.Throws(() => - { - ArgumentVerifier.ThrowIfNull(null, "args"); - }); + ArgumentVerifier.ThrowIfNull(null, "args"); + }); - Assert.Contains("args", ex.Message); - } + Assert.Contains("args", ex.Message); + } - [Fact] - public void ThrowIfNullOrEmpty_RespectsArgumentName_WhenValueIsNull() + [Fact] + public void ThrowIfNullOrEmpty_RespectsArgumentName_WhenValueIsNull() + { + var ex = Assert.Throws(() => { - var ex = Assert.Throws(() => - { - ArgumentVerifier.ThrowIfNullOrEmpty(null, "args"); - }); + ArgumentVerifier.ThrowIfNullOrEmpty(null, "args"); + }); - Assert.Contains("args", ex.Message); - } + Assert.Contains("args", ex.Message); + } - [Fact] - public void ThrowIfNullOrEmpty_RespectsArgumentName_WhenValueIsEmpty() + [Fact] + public void ThrowIfNullOrEmpty_RespectsArgumentName_WhenValueIsEmpty() + { + var ex = Assert.Throws(() => { - var ex = Assert.Throws(() => - { - ArgumentVerifier.ThrowIfNullOrEmpty(string.Empty, "args"); - }); + ArgumentVerifier.ThrowIfNullOrEmpty(string.Empty, "args"); + }); - Assert.Contains("args", ex.Message); - } + Assert.Contains("args", ex.Message); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/BulkPublishEventApiTest.cs b/test/Dapr.Client.Test/BulkPublishEventApiTest.cs index 14b946d66..95d10bcd6 100644 --- a/test/Dapr.Client.Test/BulkPublishEventApiTest.cs +++ b/test/Dapr.Client.Test/BulkPublishEventApiTest.cs @@ -11,330 +11,329 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Autogenerated = Dapr.Client.Autogen.Grpc.v1; +using Shouldly; +using Grpc.Core; +using Moq; +using Xunit; + +public class BulkPublishEventApiTest { - using System; - using System.Collections.Generic; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Autogenerated = Dapr.Client.Autogen.Grpc.v1; - using Shouldly; - using Grpc.Core; - using Moq; - using Xunit; - - public class BulkPublishEventApiTest - { - const string TestPubsubName = "testpubsubname"; - const string TestTopicName = "test"; - const string TestContentType = "application/json"; - static readonly List bulkPublishData = new List() { "hello", "world" }; + const string TestPubsubName = "testpubsubname"; + const string TestTopicName = "test"; + const string TestContentType = "application/json"; + static readonly List bulkPublishData = new List() { "hello", "world" }; - [Fact] - public async Task BulkPublishEventAsync_CanPublishTopicWithEvents() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task BulkPublishEventAsync_CanPublishTopicWithEvents() + { + await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData)); - request.Dismiss(); + request.Dismiss(); - var envelope = await request.GetRequestEnvelopeAsync(); + var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.ShouldBe(2); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe(TestTopicName); - envelope.Metadata.Count.ShouldBe(0); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); + envelope.Metadata.Count.ShouldBe(0); - var firstEntry = envelope.Entries[0]; + var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.ShouldBe("0"); - firstEntry.ContentType.ShouldBe(TestContentType); - firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.ShouldBeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); - var secondEntry = envelope.Entries[1]; + var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.ShouldBe("1"); - secondEntry.ContentType.ShouldBe(TestContentType); - secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.ShouldBeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); - // Create Response & Respond - var response = new Autogenerated.BulkPublishResponse - { - FailedEntries = { } - }; - var bulkPublishResponse = await request.CompleteWithMessageAsync(response); + // Create Response & Respond + var response = new Autogenerated.BulkPublishResponse + { + FailedEntries = { } + }; + var bulkPublishResponse = await request.CompleteWithMessageAsync(response); - // Get response and validate - bulkPublishResponse.FailedEntries.Count.ShouldBe(0); - } + // Get response and validate + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); + } - [Fact] - public async Task BulkPublishEventAsync_CanPublishTopicWithEvents_WithMetadata() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task BulkPublishEventAsync_CanPublishTopicWithEvents_WithMetadata() + { + await using var client = TestClient.CreateForDaprClient(); - var metadata = new Dictionary { { "key1", "value1" }, { "key2", "value2" } }; + var metadata = new Dictionary { { "key1", "value1" }, { "key2", "value2" } }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, - metadata)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, + metadata)); - request.Dismiss(); + request.Dismiss(); - var envelope = await request.GetRequestEnvelopeAsync(); + var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.ShouldBe(2); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe(TestTopicName); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); - var firstEntry = envelope.Entries[0]; + var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.ShouldBe("0"); - firstEntry.ContentType.ShouldBe(TestContentType); - firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.ShouldBeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); - var secondEntry = envelope.Entries[1]; + var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.ShouldBe("1"); - secondEntry.ContentType.ShouldBe(TestContentType); - secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.ShouldBeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); - // Create Response & Respond - var response = new Autogenerated.BulkPublishResponse - { - FailedEntries = { } - }; - var bulkPublishResponse = await request.CompleteWithMessageAsync(response); + // Create Response & Respond + var response = new Autogenerated.BulkPublishResponse + { + FailedEntries = { } + }; + var bulkPublishResponse = await request.CompleteWithMessageAsync(response); - // Get response and validate - bulkPublishResponse.FailedEntries.Count.ShouldBe(0); - } + // Get response and validate + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); + } - [Fact] - public async Task BulkPublishEventAsync_CanPublishTopicWithNoContent() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task BulkPublishEventAsync_CanPublishTopicWithNoContent() + { + await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, - null)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, + null)); - request.Dismiss(); + request.Dismiss(); - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.ShouldBe(2); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe(TestTopicName); - envelope.Metadata.Count.ShouldBe(0); + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); + envelope.Metadata.Count.ShouldBe(0); - var firstEntry = envelope.Entries[0]; + var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.ShouldBe("0"); - firstEntry.ContentType.ShouldBe(TestContentType); - firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.ShouldBeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); - var secondEntry = envelope.Entries[1]; + var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.ShouldBe("1"); - secondEntry.ContentType.ShouldBe(TestContentType); - secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.ShouldBeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); - // Create Response & Respond - var response = new Autogenerated.BulkPublishResponse - { - FailedEntries = { } - }; - var bulkPublishResponse = await request.CompleteWithMessageAsync(response); + // Create Response & Respond + var response = new Autogenerated.BulkPublishResponse + { + FailedEntries = { } + }; + var bulkPublishResponse = await request.CompleteWithMessageAsync(response); - // Get response and validate - bulkPublishResponse.FailedEntries.Count.ShouldBe(0); - } + // Get response and validate + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); + } - [Fact] - public async Task BulkPublishEventAsync_CanPublishTopicWithNoContent_WithMetadata() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task BulkPublishEventAsync_CanPublishTopicWithNoContent_WithMetadata() + { + await using var client = TestClient.CreateForDaprClient(); - var metadata = new Dictionary { { "key1", "value1" }, { "key2", "value2" } }; + var metadata = new Dictionary { { "key1", "value1" }, { "key2", "value2" } }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, - metadata)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, + metadata)); - request.Dismiss(); + request.Dismiss(); - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.ShouldBe(2); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe(TestTopicName); + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); - var firstEntry = envelope.Entries[0]; + var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.ShouldBe("0"); - firstEntry.ContentType.ShouldBe(TestContentType); - firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.ShouldBeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); - var secondEntry = envelope.Entries[1]; + var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.ShouldBe("1"); - secondEntry.ContentType.ShouldBe(TestContentType); - secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.ShouldBeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); - // Create Response & Respond - var response = new Autogenerated.BulkPublishResponse - { - FailedEntries = { } - }; - var bulkPublishResponse = await request.CompleteWithMessageAsync(response); + // Create Response & Respond + var response = new Autogenerated.BulkPublishResponse + { + FailedEntries = { } + }; + var bulkPublishResponse = await request.CompleteWithMessageAsync(response); - // Get response and validate - bulkPublishResponse.FailedEntries.Count.ShouldBe(0); - } + // Get response and validate + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); + } - [Fact] - public async Task BulkPublishEventAsync_CanPublishTopicWithEventsObject() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task BulkPublishEventAsync_CanPublishTopicWithEventsObject() + { + await using var client = TestClient.CreateForDaprClient(); - var bulkPublishDataObject = new Widget() { Size = "Big", Color = "Green" }; + var bulkPublishDataObject = new Widget() { Size = "Big", Color = "Green" }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, + var request = await client.CaptureGrpcRequestAsync(async daprClient => + await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, new List { bulkPublishDataObject }, null)); - request.Dismiss(); + request.Dismiss(); - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.ShouldBe(1); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe(TestTopicName); - envelope.Metadata.Count.ShouldBe(0); + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Entries.Count.ShouldBe(1); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); + envelope.Metadata.Count.ShouldBe(0); - var firstEntry = envelope.Entries[0]; + var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.ShouldBe("0"); - firstEntry.ContentType.ShouldBe(TestContentType); - firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.ShouldBeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); - // Create Response & Respond - var response = new Autogenerated.BulkPublishResponse - { - FailedEntries = { } - }; - var bulkPublishResponse = await request.CompleteWithMessageAsync(response); + // Create Response & Respond + var response = new Autogenerated.BulkPublishResponse + { + FailedEntries = { } + }; + var bulkPublishResponse = await request.CompleteWithMessageAsync(response); - // Get response and validate - bulkPublishResponse.FailedEntries.Count.ShouldBe(0); - } + // Get response and validate + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); + } - [Fact] - public async Task BulkPublishEventAsync_WithCancelledToken() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task BulkPublishEventAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - var cts = new CancellationTokenSource(); - cts.Cancel(); + var cts = new CancellationTokenSource(); + cts.Cancel(); - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, - null, cancellationToken: cts.Token); - }); - } - - [Fact] - public async Task BulkPublishEventAsync_WrapsRpcException() + await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.InnerClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, bulkPublishData, + null, cancellationToken: cts.Token); + }); + } + + [Fact] + public async Task BulkPublishEventAsync_WrapsRpcException() + { + var client = new MockClient(); - var rpcStatus = new Status(StatusCode.Internal, "not gonna work"); - var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); + var rpcStatus = new Status(StatusCode.Internal, "not gonna work"); + var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.BulkPublishEventAlpha1Async( - It.IsAny(), - It.IsAny())) - .Throws(rpcException); + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.BulkPublishEventAlpha1Async( + It.IsAny(), + It.IsAny())) + .Throws(rpcException); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, - bulkPublishData); - }); - Assert.Same(rpcException, ex.InnerException); - } - - [Fact] - public async Task BulkPublishEventAsync_FailureResponse() + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await client.DaprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, + bulkPublishData); + }); + Assert.Same(rpcException, ex.InnerException); + } + + [Fact] + public async Task BulkPublishEventAsync_FailureResponse() + { + await using var client = TestClient.CreateForDaprClient(); - var metadata = new Dictionary { { "key1", "value1" }, { "key2", "value2" } }; + var metadata = new Dictionary { { "key1", "value1" }, { "key2", "value2" } }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, - bulkPublishData, metadata); - }); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.BulkPublishEventAsync(TestPubsubName, TestTopicName, + bulkPublishData, metadata); + }); - request.Dismiss(); + request.Dismiss(); - // Create Response & Respond - var response = new Autogenerated.BulkPublishResponse + // Create Response & Respond + var response = new Autogenerated.BulkPublishResponse + { + FailedEntries = { - FailedEntries = + new Autogenerated.BulkPublishResponseFailedEntry + { + EntryId = "0", + Error = "Failed to publish", + }, + new Autogenerated.BulkPublishResponseFailedEntry { - new Autogenerated.BulkPublishResponseFailedEntry - { - EntryId = "0", - Error = "Failed to publish", - }, - new Autogenerated.BulkPublishResponseFailedEntry - { - EntryId = "1", - Error = "Failed to publish", - }, - } - }; - var bulkPublishResponse = await request.CompleteWithMessageAsync(response); + EntryId = "1", + Error = "Failed to publish", + }, + } + }; + var bulkPublishResponse = await request.CompleteWithMessageAsync(response); - // Get response and validate - bulkPublishResponse.FailedEntries[0].Entry.EntryId.ShouldBe("0"); - bulkPublishResponse.FailedEntries[0].ErrorMessage.ShouldBe("Failed to publish"); + // Get response and validate + bulkPublishResponse.FailedEntries[0].Entry.EntryId.ShouldBe("0"); + bulkPublishResponse.FailedEntries[0].ErrorMessage.ShouldBe("Failed to publish"); - bulkPublishResponse.FailedEntries[1].Entry.EntryId.ShouldBe("1"); - bulkPublishResponse.FailedEntries[1].ErrorMessage.ShouldBe("Failed to publish"); - } + bulkPublishResponse.FailedEntries[1].Entry.EntryId.ShouldBe("1"); + bulkPublishResponse.FailedEntries[1].ErrorMessage.ShouldBe("Failed to publish"); + } - private class Widget - { - public string Size { get; set; } - public string Color { get; set; } - } + private class Widget + { + public string Size { get; set; } + public string Color { get; set; } } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/ConfigurationApiTest.cs b/test/Dapr.Client.Test/ConfigurationApiTest.cs index 495026fc2..9595f0426 100644 --- a/test/Dapr.Client.Test/ConfigurationApiTest.cs +++ b/test/Dapr.Client.Test/ConfigurationApiTest.cs @@ -17,128 +17,127 @@ using Xunit; using Shouldly; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +public class ConfigurationApiTest { - public class ConfigurationApiTest + [Fact] + public async Task GetConfigurationAsync_WithAllValues_ValidateRequest() { - [Fact] - public async Task GetConfigurationAsync_WithAllValues_ValidateRequest() + await using var client = TestClient.CreateForDaprClient(); + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetConfiguration("testStore", new List() { "test_key" }, metadata); - }); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Keys.ShouldContain("test_key"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - - // Get response and validate - var invokeResponse = new Autogenerated.GetConfigurationResponse(); - invokeResponse.Items["testKey"] = new Autogenerated.ConfigurationItem() - { - Value = "testValue", - Version = "v1" - }; - - var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); - var configItem = domainResponse.Items["testKey"]; - configItem.Value.ShouldBe("testValue"); - configItem.Version.ShouldBe("v1"); - } - - [Fact] - public async Task GetConfigurationAsync_WithNullKeys_ValidateRequest() + { "key1", "value1" }, + { "key2", "value2" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetConfiguration("testStore", null, metadata); - }); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Keys.ShouldBeEmpty(); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - - // Get response and validate - var invokeResponse = new Autogenerated.GetConfigurationResponse(); - invokeResponse.Items["testKey"] = new Autogenerated.ConfigurationItem() - { - Value = "testValue", - Version = "v1" - }; - - var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); - var configItem = domainResponse.Items["testKey"]; - configItem.Value.ShouldBe("testValue"); - configItem.Version.ShouldBe("v1"); - } - - [Fact] - public async Task GetConfigurationAsync_WithNullMetadata_ValidateRequest() + return await daprClient.GetConfiguration("testStore", new List() { "test_key" }, metadata); + }); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Keys.ShouldContain("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + + // Get response and validate + var invokeResponse = new Autogenerated.GetConfigurationResponse(); + invokeResponse.Items["testKey"] = new Autogenerated.ConfigurationItem() { - await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetConfiguration("testStore", new List() { "test_key" }); - }); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Keys.ShouldContain("test_key"); - envelope.Metadata.Count.ShouldBe(0); - - // Get response and validate - var invokeResponse = new Autogenerated.GetConfigurationResponse(); - invokeResponse.Items["testKey"] = new Autogenerated.ConfigurationItem() - { - Value = "testValue", - Version = "v1" - }; - - var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); - var configItem = domainResponse.Items["testKey"]; - configItem.Value.ShouldBe("testValue"); - configItem.Version.ShouldBe("v1"); - } - - [Fact] - public async Task UnsubscribeConfiguration_ValidateRequest() + Value = "testValue", + Version = "v1" + }; + + var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); + var configItem = domainResponse.Items["testKey"]; + configItem.Value.ShouldBe("testValue"); + configItem.Version.ShouldBe("v1"); + } + + [Fact] + public async Task GetConfigurationAsync_WithNullKeys_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.UnsubscribeConfiguration("testStore", "testId"); - }); - - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Id.ShouldBe("testId"); - request.Dismiss(); - } + { "key1", "value1" }, + { "key2", "value2" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.GetConfiguration("testStore", null, metadata); + }); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Keys.ShouldBeEmpty(); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + + // Get response and validate + var invokeResponse = new Autogenerated.GetConfigurationResponse(); + invokeResponse.Items["testKey"] = new Autogenerated.ConfigurationItem() + { + Value = "testValue", + Version = "v1" + }; + + var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); + var configItem = domainResponse.Items["testKey"]; + configItem.Value.ShouldBe("testValue"); + configItem.Version.ShouldBe("v1"); + } + + [Fact] + public async Task GetConfigurationAsync_WithNullMetadata_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.GetConfiguration("testStore", new List() { "test_key" }); + }); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Keys.ShouldContain("test_key"); + envelope.Metadata.Count.ShouldBe(0); + + // Get response and validate + var invokeResponse = new Autogenerated.GetConfigurationResponse(); + invokeResponse.Items["testKey"] = new Autogenerated.ConfigurationItem() + { + Value = "testValue", + Version = "v1" + }; + + var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); + var configItem = domainResponse.Items["testKey"]; + configItem.Value.ShouldBe("testValue"); + configItem.Version.ShouldBe("v1"); + } + + [Fact] + public async Task UnsubscribeConfiguration_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.UnsubscribeConfiguration("testStore", "testId"); + }); + + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Id.ShouldBe("testId"); + request.Dismiss(); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/ConfigurationSourceTest.cs b/test/Dapr.Client.Test/ConfigurationSourceTest.cs index c381d41de..d9f59bacf 100644 --- a/test/Dapr.Client.Test/ConfigurationSourceTest.cs +++ b/test/Dapr.Client.Test/ConfigurationSourceTest.cs @@ -7,98 +7,97 @@ using Xunit; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +public class ConfigurationSourceTest { - public class ConfigurationSourceTest - { - private readonly string StoreName = "testStore"; - private readonly string SubscribeId = "testSubscribe"; + private readonly string StoreName = "testStore"; + private readonly string SubscribeId = "testSubscribe"; - [Fact] - public async Task TestStreamingConfigurationSourceCanBeRead() + [Fact] + public async Task TestStreamingConfigurationSourceCanBeRead() + { + // Standard values that we don't need to Mock. + using var cts = new CancellationTokenSource(); + var streamRequest = new Autogenerated.SubscribeConfigurationRequest() { - // Standard values that we don't need to Mock. - using var cts = new CancellationTokenSource(); - var streamRequest = new Autogenerated.SubscribeConfigurationRequest() - { - StoreName = StoreName - }; - var callOptions = new CallOptions(cancellationToken: cts.Token); - var item1 = new Autogenerated.ConfigurationItem() - { - Value = "testValue1", - Version = "V1", - }; - var item2 = new Autogenerated.ConfigurationItem() - { - Value = "testValue2", - Version = "V1", - }; - var responses = new List() - { - new Autogenerated.SubscribeConfigurationResponse() { Id = SubscribeId }, - new Autogenerated.SubscribeConfigurationResponse() { Id = SubscribeId }, - }; - responses[0].Items["testKey1"] = item1; - responses[1].Items["testKey2"] = item2; + StoreName = StoreName + }; + var callOptions = new CallOptions(cancellationToken: cts.Token); + var item1 = new Autogenerated.ConfigurationItem() + { + Value = "testValue1", + Version = "V1", + }; + var item2 = new Autogenerated.ConfigurationItem() + { + Value = "testValue2", + Version = "V1", + }; + var responses = new List() + { + new Autogenerated.SubscribeConfigurationResponse() { Id = SubscribeId }, + new Autogenerated.SubscribeConfigurationResponse() { Id = SubscribeId }, + }; + responses[0].Items["testKey1"] = item1; + responses[1].Items["testKey2"] = item2; - // Setup the Mock and actions. - var internalClient = Mock.Of(); - var responseStream = new TestAsyncStreamReader(responses, TimeSpan.FromMilliseconds(100)); - var response = new AsyncServerStreamingCall(responseStream, null, null, null, async () => await Task.Delay(TimeSpan.FromMilliseconds(1))); - Mock.Get(internalClient).Setup(client => client.SubscribeConfiguration(streamRequest, callOptions)) - .Returns(response); + // Setup the Mock and actions. + var internalClient = Mock.Of(); + var responseStream = new TestAsyncStreamReader(responses, TimeSpan.FromMilliseconds(100)); + var response = new AsyncServerStreamingCall(responseStream, null, null, null, async () => await Task.Delay(TimeSpan.FromMilliseconds(1))); + Mock.Get(internalClient).Setup(client => client.SubscribeConfiguration(streamRequest, callOptions)) + .Returns(response); - // Try and actually use the source. - var source = new SubscribeConfigurationResponse(new DaprSubscribeConfigurationSource(response)); - Dictionary readItems = new Dictionary(); - await foreach (var items in source.Source) + // Try and actually use the source. + var source = new SubscribeConfigurationResponse(new DaprSubscribeConfigurationSource(response)); + Dictionary readItems = new Dictionary(); + await foreach (var items in source.Source) + { + foreach (var item in items) { - foreach (var item in items) - { - readItems[item.Key] = new ConfigurationItem(item.Value.Value, item.Value.Version, item.Value.Metadata); - } + readItems[item.Key] = new ConfigurationItem(item.Value.Value, item.Value.Version, item.Value.Metadata); } + } - var expectedItems = new Dictionary(); - expectedItems["testKey1"] = new ConfigurationItem("testValue1", "V1", null); - expectedItems["testKey2"] = new ConfigurationItem("testValue2", "V1", null); - Assert.Equal(SubscribeId, source.Id); - Assert.Equal(expectedItems.Count, readItems.Count); - // The gRPC metadata stops us from just doing the direct list comparison. + var expectedItems = new Dictionary(); + expectedItems["testKey1"] = new ConfigurationItem("testValue1", "V1", null); + expectedItems["testKey2"] = new ConfigurationItem("testValue2", "V1", null); + Assert.Equal(SubscribeId, source.Id); + Assert.Equal(expectedItems.Count, readItems.Count); + // The gRPC metadata stops us from just doing the direct list comparison. - var expectedConfigItem1 = expectedItems["testKey1"]; - var expectedConfigItem2 = expectedItems["testKey2"]; - var readConfigItem1 = expectedItems["testKey1"]; - var readConfigItem2 = expectedItems["testKey2"]; + var expectedConfigItem1 = expectedItems["testKey1"]; + var expectedConfigItem2 = expectedItems["testKey2"]; + var readConfigItem1 = expectedItems["testKey1"]; + var readConfigItem2 = expectedItems["testKey2"]; - Assert.Equal(expectedConfigItem1.Value, readConfigItem1.Value); - Assert.Equal(expectedConfigItem1.Version, readConfigItem1.Version); - Assert.Equal(expectedConfigItem1.Metadata, readConfigItem1.Metadata); - Assert.Equal(expectedConfigItem2.Value, readConfigItem2.Value); - Assert.Equal(expectedConfigItem2.Version, readConfigItem2.Version); - Assert.Equal(expectedConfigItem2.Metadata, readConfigItem2.Metadata); - } + Assert.Equal(expectedConfigItem1.Value, readConfigItem1.Value); + Assert.Equal(expectedConfigItem1.Version, readConfigItem1.Version); + Assert.Equal(expectedConfigItem1.Metadata, readConfigItem1.Metadata); + Assert.Equal(expectedConfigItem2.Value, readConfigItem2.Value); + Assert.Equal(expectedConfigItem2.Version, readConfigItem2.Version); + Assert.Equal(expectedConfigItem2.Metadata, readConfigItem2.Metadata); + } - private class TestAsyncStreamReader : IAsyncStreamReader - { - private IEnumerator enumerator; - private TimeSpan simulatedWaitTime; + private class TestAsyncStreamReader : IAsyncStreamReader + { + private IEnumerator enumerator; + private TimeSpan simulatedWaitTime; - public TestAsyncStreamReader(IList items, TimeSpan simulatedWaitTime) - { - this.enumerator = items.GetEnumerator(); - this.simulatedWaitTime = simulatedWaitTime; - } + public TestAsyncStreamReader(IList items, TimeSpan simulatedWaitTime) + { + this.enumerator = items.GetEnumerator(); + this.simulatedWaitTime = simulatedWaitTime; + } - public T Current => enumerator.Current; + public T Current => enumerator.Current; - public async Task MoveNext(CancellationToken cancellationToken) - { - // Add a little delay to pretend we're getting responses from a server stream. - await Task.Delay(simulatedWaitTime, cancellationToken); - return enumerator.MoveNext(); - } + public async Task MoveNext(CancellationToken cancellationToken) + { + // Add a little delay to pretend we're getting responses from a server stream. + await Task.Delay(simulatedWaitTime, cancellationToken); + return enumerator.MoveNext(); } } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/CryptographyApiTest.cs b/test/Dapr.Client.Test/CryptographyApiTest.cs index a7d57a096..4bc0915e9 100644 --- a/test/Dapr.Client.Test/CryptographyApiTest.cs +++ b/test/Dapr.Client.Test/CryptographyApiTest.cs @@ -5,91 +5,90 @@ using Xunit; #pragma warning disable CS0618 // Type or member is obsolete -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +public class CryptographyApiTest { - public class CryptographyApiTest + [Fact] + public async Task EncryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException() { - [Fact] - public async Task EncryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string vaultResourceName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.EncryptAsync(vaultResourceName, - (ReadOnlyMemory)Array.Empty(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa), - CancellationToken.None)); - } + var client = new DaprClientBuilder().Build(); + const string vaultResourceName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.EncryptAsync(vaultResourceName, + (ReadOnlyMemory)Array.Empty(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa), + CancellationToken.None)); + } - [Fact] - public async Task EncryptAsync_ByteArray_KeyName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string keyName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.EncryptAsync( "myVault", - (ReadOnlyMemory) Array.Empty(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), CancellationToken.None)); - } + [Fact] + public async Task EncryptAsync_ByteArray_KeyName_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + const string keyName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.EncryptAsync( "myVault", + (ReadOnlyMemory) Array.Empty(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), CancellationToken.None)); + } - [Fact] - public async Task EncryptAsync_Stream_VaultResourceName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string vaultResourceName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.EncryptAsync(vaultResourceName, - new MemoryStream(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa), - CancellationToken.None)); - } + [Fact] + public async Task EncryptAsync_Stream_VaultResourceName_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + const string vaultResourceName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.EncryptAsync(vaultResourceName, + new MemoryStream(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa), + CancellationToken.None)); + } - [Fact] - public async Task EncryptAsync_Stream_KeyName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string keyName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.EncryptAsync("myVault", - (Stream) new MemoryStream(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), - CancellationToken.None)); - } + [Fact] + public async Task EncryptAsync_Stream_KeyName_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + const string keyName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.EncryptAsync("myVault", + (Stream) new MemoryStream(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), + CancellationToken.None)); + } - [Fact] - public async Task DecryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string vaultResourceName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.DecryptAsync(vaultResourceName, - Array.Empty(), "myKey", new DecryptionOptions(), CancellationToken.None)); - } + [Fact] + public async Task DecryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + const string vaultResourceName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.DecryptAsync(vaultResourceName, + Array.Empty(), "myKey", new DecryptionOptions(), CancellationToken.None)); + } - [Fact] - public async Task DecryptAsync_ByteArray_KeyName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string keyName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.DecryptAsync("myVault", - Array.Empty(), keyName, new DecryptionOptions(), CancellationToken.None)); - } + [Fact] + public async Task DecryptAsync_ByteArray_KeyName_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + const string keyName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.DecryptAsync("myVault", + Array.Empty(), keyName, new DecryptionOptions(), CancellationToken.None)); + } - [Fact] - public async Task DecryptAsync_Stream_VaultResourceName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string vaultResourceName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.DecryptAsync(vaultResourceName, - new MemoryStream(), "MyKey", new DecryptionOptions(), CancellationToken.None)); - } + [Fact] + public async Task DecryptAsync_Stream_VaultResourceName_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + const string vaultResourceName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.DecryptAsync(vaultResourceName, + new MemoryStream(), "MyKey", new DecryptionOptions(), CancellationToken.None)); + } - [Fact] - public async Task DecryptAsync_Stream_KeyName_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - const string keyName = ""; - //Get response and validate - await Assert.ThrowsAsync(async () => await client.DecryptAsync("myVault", - new MemoryStream(), keyName, new DecryptionOptions(), CancellationToken.None)); - } + [Fact] + public async Task DecryptAsync_Stream_KeyName_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + const string keyName = ""; + //Get response and validate + await Assert.ThrowsAsync(async () => await client.DecryptAsync("myVault", + new MemoryStream(), keyName, new DecryptionOptions(), CancellationToken.None)); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/DaprApiTokenTest.cs b/test/Dapr.Client.Test/DaprApiTokenTest.cs index e96d29d0d..6b6b32b50 100644 --- a/test/Dapr.Client.Test/DaprApiTokenTest.cs +++ b/test/Dapr.Client.Test/DaprApiTokenTest.cs @@ -11,55 +11,54 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client.Test -{ - using System.Linq; - using System.Threading.Tasks; - using Shouldly; - using Xunit; - using Autogenerated = Dapr.Client.Autogen.Grpc.v1; +namespace Dapr.Client.Test; + +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Xunit; +using Autogenerated = Dapr.Client.Autogen.Grpc.v1; - public class DaprApiTokenTest +public class DaprApiTokenTest +{ + [Fact] + public async Task DaprCall_WithApiTokenSet() { - [Fact] - public async Task DaprCall_WithApiTokenSet() + // Configure Client + await using var client = TestClient.CreateForDaprClient(c => c.UseDaprApiToken("test_token")); + + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - // Configure Client - await using var client = TestClient.CreateForDaprClient(c => c.UseDaprApiToken("test_token")); + return await daprClient.GetSecretAsync("testStore", "test_key"); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetSecretAsync("testStore", "test_key"); - }); + request.Dismiss(); - request.Dismiss(); + // Get Request and validate + await request.GetRequestEnvelopeAsync(); - // Get Request and validate - await request.GetRequestEnvelopeAsync(); + request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); + headerValues.Count().ShouldBe(1); + headerValues.First().ShouldBe("test_token"); + } - request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.Count().ShouldBe(1); - headerValues.First().ShouldBe("test_token"); - } + [Fact] + public async Task DaprCall_WithoutApiToken() + { + // Configure Client + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task DaprCall_WithoutApiToken() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - // Configure Client - await using var client = TestClient.CreateForDaprClient(); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetSecretAsync("testStore", "test_key"); - }); + return await daprClient.GetSecretAsync("testStore", "test_key"); + }); - request.Dismiss(); + request.Dismiss(); - // Get Request and validate - await request.GetRequestEnvelopeAsync(); + // Get Request and validate + await request.GetRequestEnvelopeAsync(); - request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.ShouldBeNull(); - } + request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); + headerValues.ShouldBeNull(); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs index 99fbd4972..a37fe8928 100644 --- a/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs +++ b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs @@ -14,39 +14,38 @@ using System; using Xunit; -namespace Dapr.Client +namespace Dapr.Client; + +public partial class DaprClientTest { - public partial class DaprClientTest + [Fact] + public void CreateInvokableHttpClient_WithAppId_FromDaprClient() { - [Fact] - public void CreateInvokableHttpClient_WithAppId_FromDaprClient() - { - var daprClient = new MockClient().DaprClient; - var client = daprClient.CreateInvokableHttpClient(appId: "bank"); - Assert.Equal("http://bank/", client.BaseAddress.AbsoluteUri); - } + var daprClient = new MockClient().DaprClient; + var client = daprClient.CreateInvokableHttpClient(appId: "bank"); + Assert.Equal("http://bank/", client.BaseAddress.AbsoluteUri); + } - [Fact] - public void CreateInvokableHttpClient_InvalidAppId_FromDaprClient() + [Fact] + public void CreateInvokableHttpClient_InvalidAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; + var ex = Assert.Throws(() => { - 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: ""); - }); + // 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); - } + 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; + [Fact] + public void CreateInvokableHttpClient_WithoutAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; - var client = daprClient.CreateInvokableHttpClient(); - Assert.Null(client.BaseAddress); - } + var client = daprClient.CreateInvokableHttpClient(); + Assert.Null(client.BaseAddress); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs index 58663227c..89f93aa5b 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs @@ -15,736 +15,735 @@ using System.Linq; using System.Net.Http.Headers; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Client; +using Xunit; + +// Most of the InvokeMethodAsync functionality on DaprClient is non-abstract methods that +// forward to a few different request points to create a message, or send a message and process +// its result. +// +// So we write basic tests for all of those that every parameter passing is correct, and then +// test the specialized methods in detail. +public partial class DaprClientTest { - using System; - using System.Net; - using System.Net.Http; - using System.Net.Http.Json; - using System.Text; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Client; - using Xunit; - - // Most of the InvokeMethodAsync functionality on DaprClient is non-abstract methods that - // forward to a few different request points to create a message, or send a message and process - // its result. - // - // So we write basic tests for all of those that every parameter passing is correct, and then - // test the specialized methods in detail. - public partial class DaprClientTest - { - private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) - { - // Use case sensitive settings for tests, this way we verify that the same settings are being - // used in all calls. - PropertyNameCaseInsensitive = false, + private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + // Use case sensitive settings for tests, this way we verify that the same settings are being + // used in all calls. + PropertyNameCaseInsensitive = false, + }; + + [Fact] + public async Task InvokeMethodAsync_VoidVoidNoHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + await daprClient.InvokeMethodAsync("app1", "mymethod"); + }); + + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Post); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + Assert.Null(request.Request.Content); + + await request.CompleteAsync(new HttpResponseMessage()); + } + + [Fact] + public async Task InvokeMethodAsync_VoidVoidWithHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod"); + }); + + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Put); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + Assert.Null(request.Request.Content); + + await request.CompleteAsync(new HttpResponseMessage()); + } + + [Fact] + public async Task InvokeMethodAsync_VoidResponseNoHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodAsync("app1", "mymethod"); + }); + + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Post); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + Assert.Null(request.Request.Content); + + var expected = new Widget() + { + Color = "red", }; - [Fact] - public async Task InvokeMethodAsync_VoidVoidNoHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - await daprClient.InvokeMethodAsync("app1", "mymethod"); - }); - - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Post); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - Assert.Null(request.Request.Content); - - await request.CompleteAsync(new HttpResponseMessage()); - } - - [Fact] - public async Task InvokeMethodAsync_VoidVoidWithHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod"); - }); - - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Put); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - Assert.Null(request.Request.Content); - - await request.CompleteAsync(new HttpResponseMessage()); - } - - [Fact] - public async Task InvokeMethodAsync_VoidResponseNoHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodAsync("app1", "mymethod"); - }); - - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Post); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - Assert.Null(request.Request.Content); - - var expected = new Widget() - { - Color = "red", - }; - - var actual = await request.CompleteWithJsonAsync(expected, jsonSerializerOptions); - Assert.Equal(expected.Color, actual.Color); - } - - [Fact] - public async Task InvokeMethodAsync_VoidResponseWithHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod"); - }); - - Assert.Equal(request.Request.Method, HttpMethod.Put); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - Assert.Null(request.Request.Content); - - var expected = new Widget() - { - Color = "red", - }; - - var actual = await request.CompleteWithJsonAsync(expected, jsonSerializerOptions); - Assert.Equal(expected.Color, actual.Color); - } - - [Fact] - public async Task InvokeMethodAsync_RequestVoidNoHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var data = new Widget() - { - Color = "red", - }; - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - await daprClient.InvokeMethodAsync("app1", "mymethod", data); - }); - - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Post); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - - var content = Assert.IsType(request.Request.Content); - Assert.Equal(data.GetType(), content.ObjectType); - Assert.Same(data, content.Value); - - await request.CompleteAsync(new HttpResponseMessage()); - } - - [Fact] - public async Task InvokeMethodAsync_RequestVoidWithHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var data = new Widget() - { - Color = "red", - }; - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod", data); - }); - - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Put); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + var actual = await request.CompleteWithJsonAsync(expected, jsonSerializerOptions); + Assert.Equal(expected.Color, actual.Color); + } + + [Fact] + public async Task InvokeMethodAsync_VoidResponseWithHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod"); + }); + + Assert.Equal(request.Request.Method, HttpMethod.Put); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + Assert.Null(request.Request.Content); + + var expected = new Widget() + { + Color = "red", + }; + + var actual = await request.CompleteWithJsonAsync(expected, jsonSerializerOptions); + Assert.Equal(expected.Color, actual.Color); + } + + [Fact] + public async Task InvokeMethodAsync_RequestVoidNoHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget() + { + Color = "red", + }; + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + await daprClient.InvokeMethodAsync("app1", "mymethod", data); + }); + + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Post); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + + var content = Assert.IsType(request.Request.Content); + Assert.Equal(data.GetType(), content.ObjectType); + Assert.Same(data, content.Value); + + await request.CompleteAsync(new HttpResponseMessage()); + } + + [Fact] + public async Task InvokeMethodAsync_RequestVoidWithHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget() + { + Color = "red", + }; + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod", data); + }); + + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Put); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - var content = Assert.IsType(request.Request.Content); - Assert.Equal(data.GetType(), content.ObjectType); - Assert.Same(data, content.Value); - - await request.CompleteAsync(new HttpResponseMessage()); - } - - [Fact] - public async Task InvokeMethodAsync_RequestResponseNoHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var data = new Widget() - { - Color = "red", - }; - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodAsync("app1", "mymethod", data); - }); - - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Post); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - - var content = Assert.IsType(request.Request.Content); - Assert.Equal(data.GetType(), content.ObjectType); - Assert.Same(data, content.Value); - - var actual = await request.CompleteWithJsonAsync(data, jsonSerializerOptions); - Assert.Equal(data.Color, actual.Color); - } - - [Fact] - public async Task InvokeMethodAsync_RequestResponseWithHttpMethod_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var data = new Widget() - { - Color = "red", - }; - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod", data); - }); - - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Put); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + var content = Assert.IsType(request.Request.Content); + Assert.Equal(data.GetType(), content.ObjectType); + Assert.Same(data, content.Value); + + await request.CompleteAsync(new HttpResponseMessage()); + } + + [Fact] + public async Task InvokeMethodAsync_RequestResponseNoHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget() + { + Color = "red", + }; + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodAsync("app1", "mymethod", data); + }); + + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Post); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + + var content = Assert.IsType(request.Request.Content); + Assert.Equal(data.GetType(), content.ObjectType); + Assert.Same(data, content.Value); + + var actual = await request.CompleteWithJsonAsync(data, jsonSerializerOptions); + Assert.Equal(data.Color, actual.Color); + } + + [Fact] + public async Task InvokeMethodAsync_RequestResponseWithHttpMethod_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget() + { + Color = "red", + }; + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodAsync(HttpMethod.Put, "app1", "mymethod", data); + }); + + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Put); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/app1/method/mymethod").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - var content = Assert.IsType(request.Request.Content); - Assert.Equal(data.GetType(), content.ObjectType); - Assert.Same(data, content.Value); + var content = Assert.IsType(request.Request.Content); + Assert.Equal(data.GetType(), content.ObjectType); + Assert.Same(data, content.Value); - var actual = await request.CompleteWithJsonAsync(data, jsonSerializerOptions); - Assert.Equal(data.Color, actual.Color); - } + var actual = await request.CompleteWithJsonAsync(data, jsonSerializerOptions); + Assert.Equal(data.Color, actual.Color); + } - [Fact] - public async Task CheckHealthAsync_Success() + [Fact] + public async Task CheckHealthAsync_Success() + { + await using var client = TestClient.CreateForDaprClient(c => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var request = await client.CaptureHttpRequestAsync(async daprClient => - await daprClient.CheckHealthAsync()); + var request = await client.CaptureHttpRequestAsync(async daprClient => + await daprClient.CheckHealthAsync()); - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Get); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Get); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - var result = await request.CompleteAsync(new HttpResponseMessage()); - Assert.True(result); - } + var result = await request.CompleteAsync(new HttpResponseMessage()); + Assert.True(result); + } - [Fact] - public async Task CheckHealthAsync_NotSuccess() + [Fact] + public async Task CheckHealthAsync_NotSuccess() + { + await using var client = TestClient.CreateForDaprClient(c => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var request = await client.CaptureHttpRequestAsync(async daprClient => - await daprClient.CheckHealthAsync()); + var request = await client.CaptureHttpRequestAsync(async daprClient => + await daprClient.CheckHealthAsync()); - // Get Request and validate - Assert.Equal(request.Request.Method, HttpMethod.Get); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + // Get Request and validate + Assert.Equal(request.Request.Method, HttpMethod.Get); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - var result = await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError)); - Assert.False(result); - } + var result = await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError)); + Assert.False(result); + } - [Fact] - public async Task CheckHealthAsync_WrapsHttpRequestException() + [Fact] + public async Task CheckHealthAsync_WrapsHttpRequestException() + { + await using var client = TestClient.CreateForDaprClient(c => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var request = await client.CaptureHttpRequestAsync(async daprClient => - await daprClient.CheckHealthAsync()); + var request = await client.CaptureHttpRequestAsync(async daprClient => + await daprClient.CheckHealthAsync()); - Assert.Equal(request.Request.Method, HttpMethod.Get); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + Assert.Equal(request.Request.Method, HttpMethod.Get); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - var exception = new HttpRequestException(); - var result = await request.CompleteWithExceptionAndResultAsync(exception); - Assert.False(result); - } - - [Fact] - public async Task CheckOutboundHealthAsync_Success() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); - - Assert.Equal(request.Request.Method, HttpMethod.Get); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - - var result = await request.CompleteAsync(new HttpResponseMessage()); - Assert.True(result); - } - - [Fact] - public async Task CheckOutboundHealthAsync_NotSuccess() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); - - Assert.Equal(request.Request.Method, HttpMethod.Get); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - - var result = await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError)); - Assert.False(result); - } - - [Fact] - public async Task CheckOutboundHealthAsync_WrapsRequestException() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); - - Assert.Equal(request.Request.Method, HttpMethod.Get); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); - - var result = await request.CompleteWithExceptionAndResultAsync(new HttpRequestException()); - Assert.False(result); - } - - [Fact] - public async Task WaitForSidecarAsync_SuccessWhenSidecarHealthy() - { - await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.WaitForSidecarAsync()); - - // If we don't throw, we're good. - await request.CompleteAsync(new HttpResponseMessage()); - } - - [Fact] - public async Task WaitForSidecarAsync_NotSuccessWhenSidecarNotHealthy() - { - await using var client = TestClient.CreateForDaprClient(); - using var cts = new CancellationTokenSource(); - var waitRequest = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.WaitForSidecarAsync(cts.Token)); - var healthRequest = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); - - cts.Cancel(); - - await healthRequest.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError)); - await Assert.ThrowsAsync(async () => await waitRequest.CompleteWithExceptionAsync(new TaskCanceledException())); - } - - [Fact] - public async Task InvokeMethodAsync_WrapsHttpRequestException() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); - await daprClient.InvokeMethodAsync(request); - }); - - var exception = new HttpRequestException(); - var thrown = await Assert.ThrowsAsync(async () => await request.CompleteWithExceptionAsync(exception)); - Assert.Equal("test-app", thrown.AppId); - Assert.Equal("test", thrown.MethodName); - Assert.Same(exception, thrown.InnerException); - Assert.Null(thrown.Response); - } - - [Fact] - public async Task InvokeMethodAsync_WrapsHttpRequestException_FromEnsureSuccessStatus() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); - await daprClient.InvokeMethodAsync(request); - }); - - var response = new HttpResponseMessage(HttpStatusCode.NotFound); - var thrown = await Assert.ThrowsAsync(async () => await request.CompleteAsync(response)); - Assert.Equal("test-app", thrown.AppId); - Assert.Equal("test", thrown.MethodName); - Assert.IsType(thrown.InnerException); - Assert.Same(response, thrown.Response); - } - - [Fact] - public async Task InvokeMethodAsync_WithBody_WrapsHttpRequestException() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); - return await daprClient.InvokeMethodAsync(request); - }); - - var exception = new HttpRequestException(); - var thrown = await Assert.ThrowsAsync(async () => await request.CompleteWithExceptionAsync(exception)); - Assert.Equal("test-app", thrown.AppId); - Assert.Equal("test", thrown.MethodName); - Assert.Same(exception, thrown.InnerException); - Assert.Null(thrown.Response); - } - - [Fact] - public async Task InvokeMethodAsync_WithBody_WrapsHttpRequestException_FromEnsureSuccessStatus() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); - return await daprClient.InvokeMethodAsync(request); - }); + var exception = new HttpRequestException(); + var result = await request.CompleteWithExceptionAndResultAsync(exception); + Assert.False(result); + } + + [Fact] + public async Task CheckOutboundHealthAsync_Success() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); + + Assert.Equal(request.Request.Method, HttpMethod.Get); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + + var result = await request.CompleteAsync(new HttpResponseMessage()); + Assert.True(result); + } + + [Fact] + public async Task CheckOutboundHealthAsync_NotSuccess() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); + + Assert.Equal(request.Request.Method, HttpMethod.Get); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + + var result = await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError)); + Assert.False(result); + } + + [Fact] + public async Task CheckOutboundHealthAsync_WrapsRequestException() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); + + Assert.Equal(request.Request.Method, HttpMethod.Get); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri); + + var result = await request.CompleteWithExceptionAndResultAsync(new HttpRequestException()); + Assert.False(result); + } + + [Fact] + public async Task WaitForSidecarAsync_SuccessWhenSidecarHealthy() + { + await using var client = TestClient.CreateForDaprClient(); + var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.WaitForSidecarAsync()); + + // If we don't throw, we're good. + await request.CompleteAsync(new HttpResponseMessage()); + } + + [Fact] + public async Task WaitForSidecarAsync_NotSuccessWhenSidecarNotHealthy() + { + await using var client = TestClient.CreateForDaprClient(); + using var cts = new CancellationTokenSource(); + var waitRequest = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.WaitForSidecarAsync(cts.Token)); + var healthRequest = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.CheckOutboundHealthAsync()); + + cts.Cancel(); + + await healthRequest.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError)); + await Assert.ThrowsAsync(async () => await waitRequest.CompleteWithExceptionAsync(new TaskCanceledException())); + } + + [Fact] + public async Task InvokeMethodAsync_WrapsHttpRequestException() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); + await daprClient.InvokeMethodAsync(request); + }); + + var exception = new HttpRequestException(); + var thrown = await Assert.ThrowsAsync(async () => await request.CompleteWithExceptionAsync(exception)); + Assert.Equal("test-app", thrown.AppId); + Assert.Equal("test", thrown.MethodName); + Assert.Same(exception, thrown.InnerException); + Assert.Null(thrown.Response); + } + + [Fact] + public async Task InvokeMethodAsync_WrapsHttpRequestException_FromEnsureSuccessStatus() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); + await daprClient.InvokeMethodAsync(request); + }); + + var response = new HttpResponseMessage(HttpStatusCode.NotFound); + var thrown = await Assert.ThrowsAsync(async () => await request.CompleteAsync(response)); + Assert.Equal("test-app", thrown.AppId); + Assert.Equal("test", thrown.MethodName); + Assert.IsType(thrown.InnerException); + Assert.Same(response, thrown.Response); + } + + [Fact] + public async Task InvokeMethodAsync_WithBody_WrapsHttpRequestException() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); + return await daprClient.InvokeMethodAsync(request); + }); + + var exception = new HttpRequestException(); + var thrown = await Assert.ThrowsAsync(async () => await request.CompleteWithExceptionAsync(exception)); + Assert.Equal("test-app", thrown.AppId); + Assert.Equal("test", thrown.MethodName); + Assert.Same(exception, thrown.InnerException); + Assert.Null(thrown.Response); + } + + [Fact] + public async Task InvokeMethodAsync_WithBody_WrapsHttpRequestException_FromEnsureSuccessStatus() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); + return await daprClient.InvokeMethodAsync(request); + }); - var response = new HttpResponseMessage(HttpStatusCode.NotFound); - var thrown = await Assert.ThrowsAsync(async () => await request.CompleteAsync(response)); - Assert.Equal("test-app", thrown.AppId); - Assert.Equal("test", thrown.MethodName); - Assert.IsType(thrown.InnerException); - Assert.Same(response, thrown.Response); - } - - [Fact] - public async Task InvokeMethodAsync_WrapsHttpRequestException_FromSerialization() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); - return await daprClient.InvokeMethodAsync(request); - }); - - var response = new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent("{ \"invalid\": true", Encoding.UTF8, "application/json") - }; - var thrown = await Assert.ThrowsAsync(async () => await request.CompleteAsync(response)); - Assert.Equal("test-app", thrown.AppId); - Assert.Equal("test", thrown.MethodName); - Assert.IsType(thrown.InnerException); - Assert.Same(response, thrown.Response); - } - - [Theory] - [InlineData("", "https://test-endpoint:3501/v1.0/invoke/test-app/method/")] - [InlineData("/", "https://test-endpoint:3501/v1.0/invoke/test-app/method/")] - [InlineData("mymethod", "https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod")] - [InlineData("/mymethod", "https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod")] - [InlineData("mymethod?key1=value1&key2=value2#fragment", "https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod?key1=value1&key2=value2#fragment")] - - // garbage in -> garbage out - we don't deeply inspect what you pass. - [InlineData("http://example.com", "https://test-endpoint:3501/v1.0/invoke/test-app/method/http://example.com")] - public async Task CreateInvokeMethodRequest_TransformsUrlCorrectly(string method, string expected) - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = client.InnerClient.CreateInvokeMethodRequest("test-app", method); - Assert.Equal(new Uri(expected).AbsoluteUri, request.RequestUri.AbsoluteUri); - } - - [Fact] - public async Task CreateInvokeMethodRequest_AppendQueryStringValuesCorrectly() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "mymethod", (IReadOnlyCollection>)new List> { new("a", "0"), new("b", "1") }); - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); - } - - [Fact] - public async Task CreateInvokeMethodRequest_WithoutApiToken_CreatesHttpRequestWithoutApiTokenHeader() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c - .UseGrpcEndpoint("http://localhost") - .UseHttpEndpoint("https://test-endpoint:3501") - .UseJsonSerializationOptions(this.jsonSerializerOptions) - .UseDaprApiToken(null); - }); - - var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test"); - Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); - } - - [Fact] - public async Task CreateInvokeMethodRequest_WithApiToken_CreatesHttpRequestWithApiTokenHeader() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c - .UseGrpcEndpoint("http://localhost") - .UseHttpEndpoint("https://test-endpoint:3501") - .UseJsonSerializationOptions(this.jsonSerializerOptions) - .UseDaprApiToken("test-token"); - }); - - var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test"); - Assert.True(request.Headers.TryGetValues("dapr-api-token", out var values)); - Assert.Equal("test-token", Assert.Single(values)); - } - - [Fact] - public async Task CreateInvokeMethodRequest_WithoutApiTokenAndWithData_CreatesHttpRequestWithoutApiTokenHeader() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c - .UseGrpcEndpoint("http://localhost") - .UseHttpEndpoint("https://test-endpoint:3501") - .UseJsonSerializationOptions(this.jsonSerializerOptions) - .UseDaprApiToken(null); - }); - - var data = new Widget - { - Color = "red", - }; - - var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test", data); - Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); - } - - [Fact] - public async Task CreateInvokeMethodRequest_WithApiTokenAndData_CreatesHttpRequestWithApiTokenHeader() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c - .UseGrpcEndpoint("http://localhost") - .UseHttpEndpoint("https://test-endpoint:3501") - .UseJsonSerializationOptions(this.jsonSerializerOptions) - .UseDaprApiToken("test-token"); - }); - - var data = new Widget - { - Color = "red", - }; - - var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test", data); - Assert.True(request.Headers.TryGetValues("dapr-api-token", out var values)); - Assert.Equal("test-token", Assert.Single(values)); - } - - [Fact] - public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContent() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var data = new Widget - { - Color = "red", - }; - - var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test", data); - var content = Assert.IsType(request.Content); - Assert.Equal(typeof(Widget), content.ObjectType); - Assert.Same(data, content.Value); - - // the best way to verify the usage of the correct settings object - var actual = await content.ReadFromJsonAsync(this.jsonSerializerOptions); - Assert.Equal(data.Color, actual.Color); - } - - [Fact] - public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContentWithQueryString() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var data = new Widget - { - Color = "red", - }; - - var request = client.InnerClient.CreateInvokeMethodRequest(HttpMethod.Post, "test-app", "test", new List> { new("a", "0"), new("b", "1") }, data); - - Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/test?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); - - var content = Assert.IsType(request.Content); - Assert.Equal(typeof(Widget), content.ObjectType); - Assert.Same(data, content.Value); - - // the best way to verify the usage of the correct settings object - var actual = await content.ReadFromJsonAsync(this.jsonSerializerOptions); - Assert.Equal(data.Color, actual.Color); - } + var response = new HttpResponseMessage(HttpStatusCode.NotFound); + var thrown = await Assert.ThrowsAsync(async () => await request.CompleteAsync(response)); + Assert.Equal("test-app", thrown.AppId); + Assert.Equal("test", thrown.MethodName); + Assert.IsType(thrown.InnerException); + Assert.Same(response, thrown.Response); + } + + [Fact] + public async Task InvokeMethodAsync_WrapsHttpRequestException_FromSerialization() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); + return await daprClient.InvokeMethodAsync(request); + }); + + var response = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{ \"invalid\": true", Encoding.UTF8, "application/json") + }; + var thrown = await Assert.ThrowsAsync(async () => await request.CompleteAsync(response)); + Assert.Equal("test-app", thrown.AppId); + Assert.Equal("test", thrown.MethodName); + Assert.IsType(thrown.InnerException); + Assert.Same(response, thrown.Response); + } + + [Theory] + [InlineData("", "https://test-endpoint:3501/v1.0/invoke/test-app/method/")] + [InlineData("/", "https://test-endpoint:3501/v1.0/invoke/test-app/method/")] + [InlineData("mymethod", "https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod")] + [InlineData("/mymethod", "https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod")] + [InlineData("mymethod?key1=value1&key2=value2#fragment", "https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod?key1=value1&key2=value2#fragment")] + + // garbage in -> garbage out - we don't deeply inspect what you pass. + [InlineData("http://example.com", "https://test-endpoint:3501/v1.0/invoke/test-app/method/http://example.com")] + public async Task CreateInvokeMethodRequest_TransformsUrlCorrectly(string method, string expected) + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", method); + Assert.Equal(new Uri(expected).AbsoluteUri, request.RequestUri.AbsoluteUri); + } + + [Fact] + public async Task CreateInvokeMethodRequest_AppendQueryStringValuesCorrectly() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "mymethod", (IReadOnlyCollection>)new List> { new("a", "0"), new("b", "1") }); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); + } + + [Fact] + public async Task CreateInvokeMethodRequest_WithoutApiToken_CreatesHttpRequestWithoutApiTokenHeader() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c + .UseGrpcEndpoint("http://localhost") + .UseHttpEndpoint("https://test-endpoint:3501") + .UseJsonSerializationOptions(this.jsonSerializerOptions) + .UseDaprApiToken(null); + }); + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test"); + Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); + } + + [Fact] + public async Task CreateInvokeMethodRequest_WithApiToken_CreatesHttpRequestWithApiTokenHeader() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c + .UseGrpcEndpoint("http://localhost") + .UseHttpEndpoint("https://test-endpoint:3501") + .UseJsonSerializationOptions(this.jsonSerializerOptions) + .UseDaprApiToken("test-token"); + }); + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test"); + Assert.True(request.Headers.TryGetValues("dapr-api-token", out var values)); + Assert.Equal("test-token", Assert.Single(values)); + } + + [Fact] + public async Task CreateInvokeMethodRequest_WithoutApiTokenAndWithData_CreatesHttpRequestWithoutApiTokenHeader() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c + .UseGrpcEndpoint("http://localhost") + .UseHttpEndpoint("https://test-endpoint:3501") + .UseJsonSerializationOptions(this.jsonSerializerOptions) + .UseDaprApiToken(null); + }); + + var data = new Widget + { + Color = "red", + }; + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test", data); + Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); + } + + [Fact] + public async Task CreateInvokeMethodRequest_WithApiTokenAndData_CreatesHttpRequestWithApiTokenHeader() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c + .UseGrpcEndpoint("http://localhost") + .UseHttpEndpoint("https://test-endpoint:3501") + .UseJsonSerializationOptions(this.jsonSerializerOptions) + .UseDaprApiToken("test-token"); + }); + + var data = new Widget + { + Color = "red", + }; + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test", data); + Assert.True(request.Headers.TryGetValues("dapr-api-token", out var values)); + Assert.Equal("test-token", Assert.Single(values)); + } + + [Fact] + public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContent() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget + { + Color = "red", + }; + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "test", data); + var content = Assert.IsType(request.Content); + Assert.Equal(typeof(Widget), content.ObjectType); + Assert.Same(data, content.Value); + + // the best way to verify the usage of the correct settings object + var actual = await content.ReadFromJsonAsync(this.jsonSerializerOptions); + Assert.Equal(data.Color, actual.Color); + } + + [Fact] + public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContentWithQueryString() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget + { + Color = "red", + }; + + var request = client.InnerClient.CreateInvokeMethodRequest(HttpMethod.Post, "test-app", "test", new List> { new("a", "0"), new("b", "1") }, data); + + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/test?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); + + var content = Assert.IsType(request.Content); + Assert.Equal(typeof(Widget), content.ObjectType); + Assert.Same(data, content.Value); + + // the best way to verify the usage of the correct settings object + var actual = await content.ReadFromJsonAsync(this.jsonSerializerOptions); + Assert.Equal(data.Color, actual.Color); + } - [Fact] - public async Task InvokeMethodWithoutResponse_WithExtraneousHeaders() + [Fact] + public async Task InvokeMethodWithoutResponse_WithExtraneousHeaders() + { + await using var client = TestClient.CreateForDaprClient(c => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var req = await client.CaptureHttpRequestAsync(async DaprClient => - { - var request = client.InnerClient.CreateInvokeMethodRequest(HttpMethod.Get, "test-app", "mymethod"); - request.Headers.Add("test-api-key", "test"); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "abc123"); - request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var req = await client.CaptureHttpRequestAsync(async DaprClient => + { + var request = client.InnerClient.CreateInvokeMethodRequest(HttpMethod.Get, "test-app", "mymethod"); + request.Headers.Add("test-api-key", "test"); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "abc123"); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - await DaprClient.InvokeMethodAsync(request); - }); + await DaprClient.InvokeMethodAsync(request); + }); - req.Dismiss(); + req.Dismiss(); - Assert.NotNull(req); - Assert.True(req.Request.Headers.Contains("test-api-key")); - Assert.Equal("test", req.Request.Headers.GetValues("test-api-key").First()); - Assert.True(req.Request.Headers.Contains("Authorization")); - Assert.Equal("Bearer abc123", req.Request.Headers.GetValues("Authorization").First()); - Assert.Equal("application/json", req.Request.Headers.GetValues("Accept").First()); - } - - [Fact] - public async Task InvokeMethodWithResponseAsync_ReturnsMessageWithoutCheckingStatus() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); - return await daprClient.InvokeMethodWithResponseAsync(request); - }); - - var response = await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.BadRequest)); // Non-2xx response - Assert.NotNull(response); - } - - [Fact] - public async Task InvokeMethodWithResponseAsync_WrapsHttpRequestException() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureHttpRequestAsync(async daprClient => - { - var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); - return await daprClient.InvokeMethodWithResponseAsync(request); - }); - - var exception = new HttpRequestException(); - var thrown = await Assert.ThrowsAsync(async () => await request.CompleteWithExceptionAsync(exception)); - Assert.Equal("test-app", thrown.AppId); - Assert.Equal("test", thrown.MethodName); - Assert.Same(exception, thrown.InnerException); - Assert.Null(thrown.Response); - } - - [Fact] - public async Task InvokeMethodWithResponseAsync_PreventsNonDaprRequest() - { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com"); - var ex = await Assert.ThrowsAsync(async () => - { - await client.InnerClient.InvokeMethodWithResponseAsync(request); - }); - - Assert.Equal("The provided request URI is not a Dapr service invocation URI.", ex.Message); - } - - private class Widget - { - public string Color { get; set; } - } - } -} + Assert.NotNull(req); + Assert.True(req.Request.Headers.Contains("test-api-key")); + Assert.Equal("test", req.Request.Headers.GetValues("test-api-key").First()); + Assert.True(req.Request.Headers.Contains("Authorization")); + Assert.Equal("Bearer abc123", req.Request.Headers.GetValues("Authorization").First()); + Assert.Equal("application/json", req.Request.Headers.GetValues("Accept").First()); + } + + [Fact] + public async Task InvokeMethodWithResponseAsync_ReturnsMessageWithoutCheckingStatus() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); + return await daprClient.InvokeMethodWithResponseAsync(request); + }); + + var response = await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.BadRequest)); // Non-2xx response + Assert.NotNull(response); + } + + [Fact] + public async Task InvokeMethodWithResponseAsync_WrapsHttpRequestException() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = await client.CaptureHttpRequestAsync(async daprClient => + { + var request = daprClient.CreateInvokeMethodRequest("test-app", "test"); + return await daprClient.InvokeMethodWithResponseAsync(request); + }); + + var exception = new HttpRequestException(); + var thrown = await Assert.ThrowsAsync(async () => await request.CompleteWithExceptionAsync(exception)); + Assert.Equal("test-app", thrown.AppId); + Assert.Equal("test", thrown.MethodName); + Assert.Same(exception, thrown.InnerException); + Assert.Null(thrown.Response); + } + + [Fact] + public async Task InvokeMethodWithResponseAsync_PreventsNonDaprRequest() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com"); + var ex = await Assert.ThrowsAsync(async () => + { + await client.InnerClient.InvokeMethodWithResponseAsync(request); + }); + + Assert.Equal("The provided request URI is not a Dapr service invocation URI.", ex.Message); + } + + private class Widget + { + public string Color { get; set; } + } +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs index 3ef0aed6a..dba99a720 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs @@ -26,467 +26,466 @@ using Request = Dapr.Client.Autogen.Test.Grpc.v1.Request; using Response = Dapr.Client.Autogen.Test.Grpc.v1.Response; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +public partial class DaprClientTest { - public partial class DaprClientTest + [Fact] + public async Task InvokeMethodGrpcAsync_WithCancelledToken() { - [Fact] - public async Task InvokeMethodGrpcAsync_WithCancelledToken() + await using var client = TestClient.CreateForDaprClient(c => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + c.UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var cts = new CancellationTokenSource(); - cts.Cancel(); - - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }, cancellationToken: cts.Token); - }); - } + var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeAndData() + await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }); - }); + await client.InnerClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }, cancellationToken: cts.Token); + }); + } - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.ShouldBe("test"); - envelope.Message.Method.ShouldBe("test"); - envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); + [Fact] + public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeAndData() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - // Create Response & Respond - var data = new Response() { Name = "Look, I was invoked!" }; - var response = new Autogen.Grpc.v1.InvokeResponse() - { - Data = Any.Pack(data), - }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }); + }); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); + + // Create Response & Respond + var data = new Response() { Name = "Look, I was invoked!" }; + var response = new Autogen.Grpc.v1.InvokeResponse() + { + Data = Any.Pack(data), + }; - // Validate Response - var invokedResponse = await request.CompleteWithMessageAsync(response); - invokedResponse.Name.ShouldBe("Look, I was invoked!"); - } + // Validate Response + var invokedResponse = await request.CompleteWithMessageAsync(response); + invokedResponse.Name.ShouldBe("Look, I was invoked!"); + } - [Fact] - public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeAndData_ThrowsExceptionForNonSuccess() + [Fact] + public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeAndData_ThrowsExceptionForNonSuccess() + { + var client = new MockClient(); + var data = new Response() { Name = "Look, I was invoked!" }; + var invokeResponse = new InvokeResponse { - var client = new MockClient(); - var data = new Response() { Name = "Look, I was invoked!" }; - var invokeResponse = new InvokeResponse - { - Data = Any.Pack(data), - }; + Data = Any.Pack(data), + }; - await client.Call() - .SetResponse(invokeResponse) - .Build(); + await client.Call() + .SetResponse(invokeResponse) + .Build(); - const string rpcExceptionMessage = "RPC exception"; - const StatusCode rpcStatusCode = StatusCode.Unavailable; - const string rpcStatusDetail = "Non success"; + const string rpcExceptionMessage = "RPC exception"; + const StatusCode rpcStatusCode = StatusCode.Unavailable; + const string rpcStatusDetail = "Non success"; - var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); - var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); + var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); + var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); - - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }); - }); - Assert.Same(rpcException, ex.InnerException); - } + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - [Fact] - public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeNoData() + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseJsonSerializationOptions(this.jsonSerializerOptions); - }); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodGrpcAsync("test", "test"); - }); + await client.DaprClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }); + }); + Assert.Same(rpcException, ex.InnerException); + } - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.ShouldBe("test"); - envelope.Message.Method.ShouldBe("test"); - envelope.Message.ContentType.ShouldBe(string.Empty); + [Fact] + public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeNoData() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - // Create Response & Respond - var data = new Response() { Name = "Look, I was invoked!" }; - var response = new Autogen.Grpc.v1.InvokeResponse() - { - Data = Any.Pack(data), - }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodGrpcAsync("test", "test"); + }); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(string.Empty); + + // Create Response & Respond + var data = new Response() { Name = "Look, I was invoked!" }; + var response = new Autogen.Grpc.v1.InvokeResponse() + { + Data = Any.Pack(data), + }; - // Validate Response - var invokedResponse = await request.CompleteWithMessageAsync(response); - invokedResponse.Name.ShouldBe("Look, I was invoked!"); - } + // Validate Response + var invokedResponse = await request.CompleteWithMessageAsync(response); + invokedResponse.Name.ShouldBe("Look, I was invoked!"); + } - [Fact] - public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeNoData_ThrowsExceptionNonSuccess() + [Fact] + public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeNoData_ThrowsExceptionNonSuccess() + { + var client = new MockClient(); + var data = new Response() { Name = "Look, I was invoked!" }; + var invokeResponse = new InvokeResponse { - var client = new MockClient(); - var data = new Response() { Name = "Look, I was invoked!" }; - var invokeResponse = new InvokeResponse - { - Data = Any.Pack(data), - }; + Data = Any.Pack(data), + }; - await client.Call() - .SetResponse(invokeResponse) - .Build(); + await client.Call() + .SetResponse(invokeResponse) + .Build(); - const string rpcExceptionMessage = "RPC exception"; - const StatusCode rpcStatusCode = StatusCode.Unavailable; - const string rpcStatusDetail = "Non success"; + const string rpcExceptionMessage = "RPC exception"; + const StatusCode rpcStatusCode = StatusCode.Unavailable; + const string rpcStatusDetail = "Non success"; - var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); - var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); + var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); + var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); - client.Mock - .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + client.Mock + .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.InvokeMethodGrpcAsync("test", "test"); - }); - Assert.Same(rpcException, ex.InnerException); - } + var ex = await Assert.ThrowsAsync(async () => + { + await client.DaprClient.InvokeMethodGrpcAsync("test", "test"); + }); + Assert.Same(rpcException, ex.InnerException); + } - [Fact] - public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData() + [Fact] + public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData() + { + var request = new Request() { RequestParameter = "Hello " }; + var client = new MockClient(); + var data = new Response() { Name = "Look, I was invoked!" }; + var invokeResponse = new InvokeResponse { - var request = new Request() { RequestParameter = "Hello " }; - var client = new MockClient(); - var data = new Response() { Name = "Look, I was invoked!" }; - var invokeResponse = new InvokeResponse - { - Data = Any.Pack(data), - }; + Data = Any.Pack(data), + }; - var response = - client.Call() + var response = + client.Call() .SetResponse(invokeResponse) .Build(); - client.Mock - .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) - .Returns(response); + client.Mock + .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) + .Returns(response); - await Should.NotThrowAsync(async () => await client.DaprClient.InvokeMethodGrpcAsync("test", "test", request)); - } + await Should.NotThrowAsync(async () => await client.DaprClient.InvokeMethodGrpcAsync("test", "test", request)); + } - [Fact] - public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData_ThrowsErrorNonSuccess() + [Fact] + public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData_ThrowsErrorNonSuccess() + { + var client = new MockClient(); + var data = new Response() { Name = "Look, I was invoked!" }; + var invokeResponse = new InvokeResponse { - var client = new MockClient(); - var data = new Response() { Name = "Look, I was invoked!" }; - var invokeResponse = new InvokeResponse - { - Data = Any.Pack(data), - }; + Data = Any.Pack(data), + }; - await client.Call() - .SetResponse(invokeResponse) - .Build(); + await client.Call() + .SetResponse(invokeResponse) + .Build(); - const string rpcExceptionMessage = "RPC exception"; - const StatusCode rpcStatusCode = StatusCode.Unavailable; - const string rpcStatusDetail = "Non success"; + const string rpcExceptionMessage = "RPC exception"; + const StatusCode rpcStatusCode = StatusCode.Unavailable; + const string rpcStatusDetail = "Non success"; - var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); - var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); + var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); + var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }); - }); - Assert.Same(rpcException, ex.InnerException); - } + var ex = await Assert.ThrowsAsync(async () => + { + await client.DaprClient.InvokeMethodGrpcAsync("test", "test", new Request() { RequestParameter = "Hello " }); + }); + Assert.Same(rpcException, ex.InnerException); + } - [Fact] - public async Task InvokeMethodGrpcAsync_WithNoReturnTypeAndData() + [Fact] + public async Task InvokeMethodGrpcAsync_WithNoReturnTypeAndData() + { + await using var client = TestClient.CreateForDaprClient(c => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + c.UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var invokeRequest = new Request() { RequestParameter = "Hello" }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodGrpcAsync("test", "test", invokeRequest); - }); + var invokeRequest = new Request() { RequestParameter = "Hello" }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodGrpcAsync("test", "test", invokeRequest); + }); - request.Dismiss(); + request.Dismiss(); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.ShouldBe("test"); - envelope.Message.Method.ShouldBe("test"); - envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); - var actual = envelope.Message.Data.Unpack(); - Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); - } + var actual = envelope.Message.Data.Unpack(); + Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); + } - [Fact] - public async Task InvokeMethodGrpcAsync_WithReturnTypeAndData() + [Fact] + public async Task InvokeMethodGrpcAsync_WithReturnTypeAndData() + { + await using var client = TestClient.CreateForDaprClient(c => { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + c.UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var invokeRequest = new Request() { RequestParameter = "Hello " }; - var invokeResponse = new Response { Name = "Look, I was invoked!" }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeMethodGrpcAsync("test", "test", invokeRequest); - }); + var invokeRequest = new Request() { RequestParameter = "Hello " }; + var invokeResponse = new Response { Name = "Look, I was invoked!" }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.InvokeMethodGrpcAsync("test", "test", invokeRequest); + }); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.ShouldBe("test"); - envelope.Message.Method.ShouldBe("test"); - envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); - var actual = envelope.Message.Data.Unpack(); - Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); + var actual = envelope.Message.Data.Unpack(); + Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); - // Create Response & Respond - var data = new Response() { Name = "Look, I was invoked!" }; - var response = new Autogen.Grpc.v1.InvokeResponse() - { - Data = Any.Pack(data), - }; + // Create Response & Respond + var data = new Response() { Name = "Look, I was invoked!" }; + var response = new Autogen.Grpc.v1.InvokeResponse() + { + Data = Any.Pack(data), + }; - // Validate Response - var invokedResponse = await request.CompleteWithMessageAsync(response); - invokedResponse.Name.ShouldBe(invokeResponse.Name); - } + // Validate Response + var invokedResponse = await request.CompleteWithMessageAsync(response); + invokedResponse.Name.ShouldBe(invokeResponse.Name); + } - [Fact] - public async Task InvokeMethodGrpcAsync_AppCallback_SayHello() - { - // Configure Client - var httpClient = new AppCallbackClient(new DaprAppCallbackService()); - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions() { HttpClient = httpClient, }) - .UseJsonSerializationOptions(this.jsonSerializerOptions) - .Build(); + [Fact] + public async Task InvokeMethodGrpcAsync_AppCallback_SayHello() + { + // Configure Client + var httpClient = new AppCallbackClient(new DaprAppCallbackService()); + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions() { HttpClient = httpClient, }) + .UseJsonSerializationOptions(this.jsonSerializerOptions) + .Build(); - var request = new Request() { RequestParameter = "Look, I was invoked!" }; + var request = new Request() { RequestParameter = "Look, I was invoked!" }; - var response = await daprClient.InvokeMethodGrpcAsync("test", "SayHello", request); + var response = await daprClient.InvokeMethodGrpcAsync("test", "SayHello", request); - response.Name.ShouldBe("Hello Look, I was invoked!"); - } + response.Name.ShouldBe("Hello Look, I was invoked!"); + } - [Fact] - public async Task InvokeMethodGrpcAsync_AppCallback_RepeatedField() - { - // Configure Client - var httpClient = new AppCallbackClient(new DaprAppCallbackService()); - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions() { HttpClient = httpClient, }) - .UseJsonSerializationOptions(this.jsonSerializerOptions) - .Build(); + [Fact] + public async Task InvokeMethodGrpcAsync_AppCallback_RepeatedField() + { + // Configure Client + var httpClient = new AppCallbackClient(new DaprAppCallbackService()); + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions() { HttpClient = httpClient, }) + .UseJsonSerializationOptions(this.jsonSerializerOptions) + .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.InvokeMethodGrpcAsync("test", "TestRun", testRun); + + response.Tests.Count.ShouldBe(3); + response.Tests[0].Name.ShouldBe("test1"); + response.Tests[1].Name.ShouldBe("test2"); + response.Tests[2].Name.ShouldBe("test3"); + } - 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" }); + [Fact] + public async Task InvokeMethodGrpcAsync_AppCallback_UnexpectedMethod() + { + // Configure Client + var httpClient = new AppCallbackClient(new DaprAppCallbackService()); + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions() { HttpClient = httpClient, }) + .UseJsonSerializationOptions(this.jsonSerializerOptions) + .Build(); - var response = await daprClient.InvokeMethodGrpcAsync("test", "TestRun", testRun); + var request = new Request() { RequestParameter = "Look, I was invoked!" }; - response.Tests.Count.ShouldBe(3); - response.Tests[0].Name.ShouldBe("test1"); - response.Tests[1].Name.ShouldBe("test2"); - response.Tests[2].Name.ShouldBe("test3"); - } + var response = await daprClient.InvokeMethodGrpcAsync("test", "not-existing", request); - [Fact] - public async Task InvokeMethodGrpcAsync_AppCallback_UnexpectedMethod() - { - // Configure Client - var httpClient = new AppCallbackClient(new DaprAppCallbackService()); - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions() { HttpClient = httpClient, }) - .UseJsonSerializationOptions(this.jsonSerializerOptions) - .Build(); + response.Name.ShouldBe("unexpected"); + } - var request = new Request() { RequestParameter = "Look, I was invoked!" }; - var response = await daprClient.InvokeMethodGrpcAsync("test", "not-existing", request); + [Fact] + public async Task GetMetadataAsync_WrapsRpcException() + { + var client = new MockClient(); - response.Name.ShouldBe("unexpected"); - } + const string rpcExceptionMessage = "RPC exception"; + const StatusCode rpcStatusCode = StatusCode.Unavailable; + const string rpcStatusDetail = "Non success"; + var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); + var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); - [Fact] - public async Task GetMetadataAsync_WrapsRpcException() - { - var client = new MockClient(); + client.Mock + .Setup(m => m.GetMetadataAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - const string rpcExceptionMessage = "RPC exception"; - const StatusCode rpcStatusCode = StatusCode.Unavailable; - const string rpcStatusDetail = "Non success"; + var ex = await Assert.ThrowsAsync(async () => + { + await client.DaprClient.GetMetadataAsync(default); + }); + Assert.Same(rpcException, ex.InnerException); + } - var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); - var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); + [Fact] + public async Task GetMetadataAsync_WithReturnTypeAndData() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - client.Mock - .Setup(m => m.GetMetadataAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.GetMetadataAsync(default); + }); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.GetMetadataAsync(default); - }); - Assert.Same(rpcException, ex.InnerException); - } - [Fact] - public async Task GetMetadataAsync_WithReturnTypeAndData() + // Create Response & Respond + var response = new Autogen.Grpc.v1.GetMetadataResponse() { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + ActorRuntime = new(), + Id = "testId", + }; + response.ActorRuntime.ActiveActors.Add(new ActiveActorsCount { Type = "testType", Count = 1 }); + response.RegisteredComponents.Add(new RegisteredComponents { Name = "testName", Type = "testType", Version = "V1" }); + response.ExtendedMetadata.Add("e1", "v1"); + + // Validate Response + var metadata = await request.CompleteWithMessageAsync(response); + metadata.Id.ShouldBe("testId"); + metadata.Extended.ShouldContain(new System.Collections.Generic.KeyValuePair("e1", "v1")); + metadata.Actors.ShouldContain(actors => actors.Count == 1 && actors.Type == "testType"); + metadata.Components.ShouldContain(components => components.Name == "testName" && components.Type == "testType" && components.Version == "V1" && components.Capabilities.Length == 0); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetMetadataAsync(default); - }); + [Fact] + public async Task SetMetadataAsync_WrapsRpcException() + { + var client = new MockClient(); + const string rpcExceptionMessage = "RPC exception"; + const StatusCode rpcStatusCode = StatusCode.Unavailable; + const string rpcStatusDetail = "Non success"; - // Create Response & Respond - var response = new Autogen.Grpc.v1.GetMetadataResponse() - { - ActorRuntime = new(), - Id = "testId", - }; - response.ActorRuntime.ActiveActors.Add(new ActiveActorsCount { Type = "testType", Count = 1 }); - response.RegisteredComponents.Add(new RegisteredComponents { Name = "testName", Type = "testType", Version = "V1" }); - response.ExtendedMetadata.Add("e1", "v1"); - - // Validate Response - var metadata = await request.CompleteWithMessageAsync(response); - metadata.Id.ShouldBe("testId"); - metadata.Extended.ShouldContain(new System.Collections.Generic.KeyValuePair("e1", "v1")); - metadata.Actors.ShouldContain(actors => actors.Count == 1 && actors.Type == "testType"); - metadata.Components.ShouldContain(components => components.Name == "testName" && components.Type == "testType" && components.Version == "V1" && components.Capabilities.Length == 0); - } + var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); + var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); + + client.Mock + .Setup(m => m.SetMetadataAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - [Fact] - public async Task SetMetadataAsync_WrapsRpcException() + var ex = await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.DaprClient.SetMetadataAsync("testName", "", default); + }); + Assert.Same(rpcException, ex.InnerException); + } - const string rpcExceptionMessage = "RPC exception"; - const StatusCode rpcStatusCode = StatusCode.Unavailable; - const string rpcStatusDetail = "Non success"; + [Fact] + public async Task SetMetadataAsync_WithReturnTypeAndData() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseJsonSerializationOptions(this.jsonSerializerOptions); + }); - var rpcStatus = new Status(rpcStatusCode, rpcStatusDetail); - var rpcException = new RpcException(rpcStatus, new Metadata(), rpcExceptionMessage); + var request = await client.CaptureGrpcRequestAsync(daprClient => + { + return daprClient.SetMetadataAsync("test", "testv", default); + }); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Key.ShouldBe("test"); + envelope.Value.ShouldBe("testv"); - client.Mock - .Setup(m => m.SetMetadataAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + await request.CompleteWithMessageAsync(new Empty()); + + } - var ex = await Assert.ThrowsAsync(async () => + // Test implementation of the AppCallback.AppCallbackBase service + private class DaprAppCallbackService : AppCallback.Autogen.Grpc.v1.AppCallback.AppCallbackBase + { + public override Task OnInvoke(InvokeRequest request, ServerCallContext context) + { + return request.Method switch { - await client.DaprClient.SetMetadataAsync("testName", "", default); - }); - Assert.Same(rpcException, ex.InnerException); + "SayHello" => SayHello(request), + "TestRun" => TestRun(request), + _ => Task.FromResult(new InvokeResponse() + { + Data = Any.Pack(new Response() { Name = $"unexpected" }), + }), + }; } - [Fact] - public async Task SetMetadataAsync_WithReturnTypeAndData() + private Task SayHello(InvokeRequest request) { - await using var client = TestClient.CreateForDaprClient(c => - { - c.UseJsonSerializationOptions(this.jsonSerializerOptions); - }); + var helloRequest = request.Data.Unpack(); + var helloResponse = new Response() { Name = $"Hello {helloRequest.RequestParameter}" }; - var request = await client.CaptureGrpcRequestAsync(daprClient => + return Task.FromResult(new InvokeResponse() { - return daprClient.SetMetadataAsync("test", "testv", default); + Data = Any.Pack(helloResponse), }); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Key.ShouldBe("test"); - envelope.Value.ShouldBe("testv"); - - await request.CompleteWithMessageAsync(new Empty()); - } - // Test implementation of the AppCallback.AppCallbackBase service - private class DaprAppCallbackService : AppCallback.Autogen.Grpc.v1.AppCallback.AppCallbackBase + private Task TestRun(InvokeRequest request) { - public override Task OnInvoke(InvokeRequest request, ServerCallContext context) - { - return request.Method switch - { - "SayHello" => SayHello(request), - "TestRun" => TestRun(request), - _ => Task.FromResult(new InvokeResponse() - { - Data = Any.Pack(new Response() { Name = $"unexpected" }), - }), - }; - } - - private Task SayHello(InvokeRequest request) + var echoRequest = request.Data.Unpack(); + return Task.FromResult(new InvokeResponse() { - var helloRequest = request.Data.Unpack(); - var helloResponse = new Response() { Name = $"Hello {helloRequest.RequestParameter}" }; - - return Task.FromResult(new InvokeResponse() - { - Data = Any.Pack(helloResponse), - }); - } - - private Task TestRun(InvokeRequest request) - { - var echoRequest = request.Data.Unpack(); - return Task.FromResult(new InvokeResponse() - { - Data = Any.Pack(echoRequest), - }); - } + Data = Any.Pack(echoRequest), + }); } } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/DaprClientTest.cs b/test/Dapr.Client.Test/DaprClientTest.cs index e280728c2..b825375b8 100644 --- a/test/Dapr.Client.Test/DaprClientTest.cs +++ b/test/Dapr.Client.Test/DaprClientTest.cs @@ -15,86 +15,85 @@ using System.Threading.Tasks; using Xunit; -namespace Dapr.Client +namespace Dapr.Client; + +public partial class DaprClientTest { - public partial class DaprClientTest + [Fact] + public void CreateInvokeHttpClient_WithAppId() { - [Fact] - public void CreateInvokeHttpClient_WithAppId() - { - var client = DaprClient.CreateInvokeHttpClient(appId: "bank", daprEndpoint: "http://localhost:3500"); - Assert.Equal("http://bank/", client.BaseAddress.AbsoluteUri); - } + var client = DaprClient.CreateInvokeHttpClient(appId: "bank", daprEndpoint: "http://localhost:3500"); + Assert.Equal("http://bank/", client.BaseAddress.AbsoluteUri); + } - [Fact] - public void CreateInvokeHttpClient_InvalidAppId() - { - var ex = Assert.Throws(() => - { - // The appId needs to be something that can be used as hostname in a URI. - _ = DaprClient.CreateInvokeHttpClient(appId: ""); - }); + [Fact] + public void CreateInvokeHttpClient_InvalidAppId() + { + var ex = Assert.Throws(() => + { + // The appId needs to be something that can be used as hostname in a URI. + _ = DaprClient.CreateInvokeHttpClient(appId: ""); + }); - Assert.Contains("The appId must be a valid hostname.", ex.Message); - Assert.IsType(ex.InnerException); - } + Assert.Contains("The appId must be a valid hostname.", ex.Message); + Assert.IsType(ex.InnerException); + } - [Fact] - public void CreateInvokeHttpClient_WithoutAppId() - { - var client = DaprClient.CreateInvokeHttpClient(daprEndpoint: "http://localhost:3500"); - Assert.Null(client.BaseAddress); - } + [Fact] + public void CreateInvokeHttpClient_WithoutAppId() + { + var client = DaprClient.CreateInvokeHttpClient(daprEndpoint: "http://localhost:3500"); + Assert.Null(client.BaseAddress); + } - [Fact] - public void CreateInvokeHttpClient_InvalidDaprEndpoint_InvalidFormat() - { - Assert.Throws(() => - { - _ = DaprClient.CreateInvokeHttpClient(daprEndpoint: ""); - }); + [Fact] + public void CreateInvokeHttpClient_InvalidDaprEndpoint_InvalidFormat() + { + Assert.Throws(() => + { + _ = DaprClient.CreateInvokeHttpClient(daprEndpoint: ""); + }); + + // Exception message comes from the runtime, not validating it here. + } - // Exception message comes from the runtime, not validating it here. - } + [Fact] + public void CreateInvokeHttpClient_InvalidDaprEndpoint_InvalidScheme() + { + var ex = Assert.Throws(() => + { + _ = DaprClient.CreateInvokeHttpClient(daprEndpoint: "ftp://localhost:3500"); + }); - [Fact] - public void CreateInvokeHttpClient_InvalidDaprEndpoint_InvalidScheme() - { - var ex = Assert.Throws(() => - { - _ = DaprClient.CreateInvokeHttpClient(daprEndpoint: "ftp://localhost:3500"); - }); + Assert.Contains("The URI scheme of the Dapr endpoint must be http or https.", ex.Message); + } - Assert.Contains("The URI scheme of the Dapr endpoint must be http or https.", ex.Message); - } + [Fact] + public void GetDaprApiTokenHeader_ApiTokenSet_SetsApiTokenHeader() + { + var token = "test_token"; + var entry = DaprClient.GetDaprApiTokenHeader(token); + Assert.NotNull(entry); + Assert.Equal("test_token", entry.Value.Value); + } - [Fact] - public void GetDaprApiTokenHeader_ApiTokenSet_SetsApiTokenHeader() - { - var token = "test_token"; - var entry = DaprClient.GetDaprApiTokenHeader(token); - Assert.NotNull(entry); - Assert.Equal("test_token", entry.Value.Value); - } + [Fact] + public void GetDaprApiTokenHeader_ApiTokenNotSet_NullApiTokenHeader() + { + var entry = DaprClient.GetDaprApiTokenHeader(null); + Assert.Equal(default, entry); + } - [Fact] - public void GetDaprApiTokenHeader_ApiTokenNotSet_NullApiTokenHeader() - { - var entry = DaprClient.GetDaprApiTokenHeader(null); - Assert.Equal(default, entry); - } + [Fact] + public async Task TestShutdownApi() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task TestShutdownApi() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.ShutdownSidecarAsync(); - }); + await daprClient.ShutdownSidecarAsync(); + }); - request.Dismiss(); - } + request.Dismiss(); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/DistributedLockApiTest.cs b/test/Dapr.Client.Test/DistributedLockApiTest.cs index 1935a5431..ad5775a98 100644 --- a/test/Dapr.Client.Test/DistributedLockApiTest.cs +++ b/test/Dapr.Client.Test/DistributedLockApiTest.cs @@ -17,80 +17,79 @@ using Shouldly; using System; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +[System.Obsolete] +public class DistributedLockApiTest { - [System.Obsolete] - public class DistributedLockApiTest + [Fact] + public async Task TryLockAsync_WithAllValues_ValidateRequest() { - [Fact] - public async Task TryLockAsync_WithAllValues_ValidateRequest() + await using var client = TestClient.CreateForDaprClient(); + string storeName = "redis"; + string resourceId = "resourceId"; + string lockOwner = "owner1"; + Int32 expiryInSeconds = 1000; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); - string storeName = "redis"; - string resourceId = "resourceId"; - string lockOwner = "owner1"; - Int32 expiryInSeconds = 1000; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.Lock(storeName, resourceId, lockOwner, expiryInSeconds); - }); + return await daprClient.Lock(storeName, resourceId, lockOwner, expiryInSeconds); + }); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("redis"); - envelope.ResourceId.ShouldBe("resourceId"); - envelope.LockOwner.ShouldBe("owner1"); - envelope.ExpiryInSeconds.ShouldBe(1000); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("redis"); + envelope.ResourceId.ShouldBe("resourceId"); + envelope.LockOwner.ShouldBe("owner1"); + envelope.ExpiryInSeconds.ShouldBe(1000); - // Get response and validate - var invokeResponse = new Autogenerated.TryLockResponse{ - Success = true - }; + // Get response and validate + var invokeResponse = new Autogenerated.TryLockResponse{ + Success = true + }; - var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); - domainResponse.Success.ShouldBe(true); - } + var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); + domainResponse.Success.ShouldBe(true); + } - [Fact] - public async Task TryLockAsync_WithAllValues_ArgumentVerifierException() - { - var client = new DaprClientBuilder().Build(); - string storeName = "redis"; - string resourceId = "resourceId"; - string lockOwner = "owner1"; - Int32 expiryInSeconds = 0; - // Get response and validate - await Assert.ThrowsAsync(async () => await client.Lock(storeName, resourceId, lockOwner, expiryInSeconds)); - } + [Fact] + public async Task TryLockAsync_WithAllValues_ArgumentVerifierException() + { + var client = new DaprClientBuilder().Build(); + string storeName = "redis"; + string resourceId = "resourceId"; + string lockOwner = "owner1"; + Int32 expiryInSeconds = 0; + // Get response and validate + await Assert.ThrowsAsync(async () => await client.Lock(storeName, resourceId, lockOwner, expiryInSeconds)); + } - //Tests For Unlock API + //Tests For Unlock API - [Fact] - public async Task UnLockAsync_WithAllValues_ValidateRequest() - { - await using var client = TestClient.CreateForDaprClient(); - string storeName = "redis"; - string resourceId = "resourceId"; - string lockOwner = "owner1"; + [Fact] + public async Task UnLockAsync_WithAllValues_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + string storeName = "redis"; + string resourceId = "resourceId"; + string lockOwner = "owner1"; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.Unlock(storeName, resourceId, lockOwner); - }); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.Unlock(storeName, resourceId, lockOwner); + }); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("redis"); - envelope.ResourceId.ShouldBe("resourceId"); - envelope.LockOwner.ShouldBe("owner1"); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("redis"); + envelope.ResourceId.ShouldBe("resourceId"); + envelope.LockOwner.ShouldBe("owner1"); - // Get response and validate - var invokeResponse = new Autogenerated.UnlockResponse{ - Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist - }; + // Get response and validate + var invokeResponse = new Autogenerated.UnlockResponse{ + Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist + }; - var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); - domainResponse.status.ShouldBe(LockStatus.LockDoesNotExist); - } + var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); + domainResponse.status.ShouldBe(LockStatus.LockDoesNotExist); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs b/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs index 83c4354f9..e2eb395c7 100644 --- a/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs +++ b/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs @@ -1,38 +1,37 @@ using System.Runtime.Serialization; using Xunit; -namespace Dapr.Client.Test.Extensions +namespace Dapr.Client.Test.Extensions; + +public class EnumExtensionTest { - public class EnumExtensionTest + [Fact] + public void GetValueFromEnumMember_RedResolvesAsExpected() { - [Fact] - public void GetValueFromEnumMember_RedResolvesAsExpected() - { - var value = TestEnum.Red.GetValueFromEnumMember(); - Assert.Equal("red", value); - } - - [Fact] - public void GetValueFromEnumMember_YellowResolvesAsExpected() - { - var value = TestEnum.Yellow.GetValueFromEnumMember(); - Assert.Equal("YELLOW", value); - } + var value = TestEnum.Red.GetValueFromEnumMember(); + Assert.Equal("red", value); + } - [Fact] - public void GetValueFromEnumMember_BlueResolvesAsExpected() - { - var value = TestEnum.Blue.GetValueFromEnumMember(); - Assert.Equal("Blue", value); - } + [Fact] + public void GetValueFromEnumMember_YellowResolvesAsExpected() + { + var value = TestEnum.Yellow.GetValueFromEnumMember(); + Assert.Equal("YELLOW", value); } - public enum TestEnum + [Fact] + public void GetValueFromEnumMember_BlueResolvesAsExpected() { - [EnumMember(Value = "red")] - Red, - [EnumMember(Value = "YELLOW")] - Yellow, - Blue + var value = TestEnum.Blue.GetValueFromEnumMember(); + Assert.Equal("Blue", value); } } + +public enum TestEnum +{ + [EnumMember(Value = "red")] + Red, + [EnumMember(Value = "YELLOW")] + Yellow, + Blue +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs b/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs index 7b93c1c91..4f5bdc49c 100644 --- a/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs +++ b/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs @@ -2,62 +2,61 @@ using System.Net.Http; using Xunit; -namespace Dapr.Client.Test.Extensions +namespace Dapr.Client.Test.Extensions; + +public class HttpExtensionTest { - public class HttpExtensionTest + [Fact] + public void AddQueryParameters_ReturnsEmptyQueryStringWithNullParameters() { - [Fact] - public void AddQueryParameters_ReturnsEmptyQueryStringWithNullParameters() - { - const string uri = "https://localhost/mypath"; - var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); - var updatedUri = httpRq.RequestUri.AddQueryParameters(null); - Assert.Equal(uri, updatedUri.AbsoluteUri); - } + const string uri = "https://localhost/mypath"; + var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); + var updatedUri = httpRq.RequestUri.AddQueryParameters(null); + Assert.Equal(uri, updatedUri.AbsoluteUri); + } - [Fact] - public void AddQueryParameters_ReturnsOriginalQueryStringWithNullParameters() - { - const string uri = "https://localhost/mypath?a=0&b=1"; - var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); - var updatedUri = httpRq.RequestUri.AddQueryParameters(null); - Assert.Equal(uri, updatedUri.AbsoluteUri); - } + [Fact] + public void AddQueryParameters_ReturnsOriginalQueryStringWithNullParameters() + { + const string uri = "https://localhost/mypath?a=0&b=1"; + var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); + var updatedUri = httpRq.RequestUri.AddQueryParameters(null); + Assert.Equal(uri, updatedUri.AbsoluteUri); + } - [Fact] - public void AddQueryParameters_BuildsQueryString() + [Fact] + public void AddQueryParameters_BuildsQueryString() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath?a=0"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> { - var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath?a=0"); - var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> - { - new("test", "value") - }); - Assert.Equal("https://localhost/mypath?a=0&test=value", updatedUri.AbsoluteUri); - } + new("test", "value") + }); + Assert.Equal("https://localhost/mypath?a=0&test=value", updatedUri.AbsoluteUri); + } - [Fact] - public void AddQueryParameters_BuildQueryStringWithDuplicateKeys() + [Fact] + public void AddQueryParameters_BuildQueryStringWithDuplicateKeys() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> { - var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); - var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> - { - new("test", "1"), - new("test", "2"), - new("test", "3") - }); - Assert.Equal("https://localhost/mypath?test=1&test=2&test=3", updatedUri.AbsoluteUri); - } + new("test", "1"), + new("test", "2"), + new("test", "3") + }); + Assert.Equal("https://localhost/mypath?test=1&test=2&test=3", updatedUri.AbsoluteUri); + } - [Fact] - public void AddQueryParameters_EscapeSpacesInValues() + [Fact] + public void AddQueryParameters_EscapeSpacesInValues() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> { - var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); - var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> - { - new("name1", "John Doe"), - new("name2", "Jane Doe") - }); - Assert.Equal("https://localhost/mypath?name1=John%20Doe&name2=Jane%20Doe", updatedUri.AbsoluteUri); - } + new("name1", "John Doe"), + new("name2", "Jane Doe") + }); + Assert.Equal("https://localhost/mypath?name1=John%20Doe&name2=Jane%20Doe", updatedUri.AbsoluteUri); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/InvocationHandlerTests.cs b/test/Dapr.Client.Test/InvocationHandlerTests.cs index b171ca399..e6cfc2231 100644 --- a/test/Dapr.Client.Test/InvocationHandlerTests.cs +++ b/test/Dapr.Client.Test/InvocationHandlerTests.cs @@ -22,230 +22,229 @@ #nullable enable -namespace Dapr.Client +namespace Dapr.Client; + +public class InvocationHandlerTests { - public class InvocationHandlerTests + [Fact] + public void DaprEndpoint_InvalidScheme() { - [Fact] - public void DaprEndpoint_InvalidScheme() + var handler = new InvocationHandler(); + var ex = Assert.Throws(() => { - var handler = new InvocationHandler(); - var ex = Assert.Throws(() => - { - handler.DaprEndpoint = "ftp://localhost:3500"; - }); + handler.DaprEndpoint = "ftp://localhost:3500"; + }); - Assert.Contains("The URI scheme of the Dapr endpoint must be http or https.", ex.Message); - } + Assert.Contains("The URI scheme of the Dapr endpoint must be http or https.", ex.Message); + } - [Fact] - public void DaprEndpoint_InvalidUri() + [Fact] + public void DaprEndpoint_InvalidUri() + { + var handler = new InvocationHandler(); + Assert.Throws(() => { - var handler = new InvocationHandler(); - Assert.Throws(() => - { - handler.DaprEndpoint = ""; - }); + handler.DaprEndpoint = ""; + }); - // Exception message comes from the runtime, not validating it here - } + // Exception message comes from the runtime, not validating it here + } - [Fact] - public void TryRewriteUri_FailsForNullUri() - { - var handler = new InvocationHandler(); - Assert.False(handler.TryRewriteUri(null!, out var rewritten)); - Assert.Null(rewritten); - } + [Fact] + public void TryRewriteUri_FailsForNullUri() + { + var handler = new InvocationHandler(); + Assert.False(handler.TryRewriteUri(null!, out var rewritten)); + Assert.Null(rewritten); + } - [Fact] - public void TryRewriteUri_FailsForBadScheme() - { - var uri = new Uri("ftp://test", UriKind.Absolute); + [Fact] + public void TryRewriteUri_FailsForBadScheme() + { + var uri = new Uri("ftp://test", UriKind.Absolute); - var handler = new InvocationHandler(); - Assert.False(handler.TryRewriteUri(uri, out var rewritten)); - Assert.Null(rewritten); - } + var handler = new InvocationHandler(); + Assert.False(handler.TryRewriteUri(uri, out var rewritten)); + Assert.Null(rewritten); + } - [Fact] - public void TryRewriteUri_FailsForRelativeUris() - { - var uri = new Uri("test", UriKind.Relative); + [Fact] + public void TryRewriteUri_FailsForRelativeUris() + { + var uri = new Uri("test", UriKind.Relative); - var handler = new InvocationHandler(); - Assert.False(handler.TryRewriteUri(uri, out var rewritten)); - Assert.Null(rewritten); - } + var handler = new InvocationHandler(); + Assert.False(handler.TryRewriteUri(uri, out var rewritten)); + Assert.Null(rewritten); + } - [Theory] - [InlineData(null, "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("bank", "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("Bank", "http://bank", "https://some.host:3499/v1.0/invoke/Bank/method/")] - [InlineData("invalid", "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData(null, "http://Bank", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("Bank", "http://Bank", "https://some.host:3499/v1.0/invoke/Bank/method/")] - [InlineData("bank", "http://Bank", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("invalid", "http://Bank", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData(null, "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("bank", "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("invalid", "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData(null, "http://Bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("Bank", "http://Bank:3939", "https://some.host:3499/v1.0/invoke/Bank/method/")] - [InlineData("invalid", "http://Bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData(null, "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] - [InlineData("app-id.with.dots", "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] - [InlineData("invalid", "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] - [InlineData(null, "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] - [InlineData("App-id.with.dots", "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/App-id.with.dots/method/")] - [InlineData("invalid", "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] - [InlineData(null, "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("bank", "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("invalid", "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData(null, "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData("Bank", "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/Bank/method/")] - [InlineData("invalid", "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] - [InlineData(null, "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] - [InlineData("bank", "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] - [InlineData("invalid", "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] - [InlineData(null, "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] - [InlineData("Bank", "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/Bank/method/some/path")] - [InlineData("invalid", "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] - [InlineData(null, "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] - [InlineData("bank", "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] - [InlineData("invalid", "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] - [InlineData(null, "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] - [InlineData("Bank", "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/Bank/method/some/path?q=test&p=another#fragment")] - [InlineData("invalid", "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] - public void TryRewriteUri_WithNoAppId_RewritesUriToDaprInvoke(string? appId, string uri, string expected) + [Theory] + [InlineData(null, "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("bank", "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("Bank", "http://bank", "https://some.host:3499/v1.0/invoke/Bank/method/")] + [InlineData("invalid", "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData(null, "http://Bank", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("Bank", "http://Bank", "https://some.host:3499/v1.0/invoke/Bank/method/")] + [InlineData("bank", "http://Bank", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("invalid", "http://Bank", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData(null, "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("bank", "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("invalid", "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData(null, "http://Bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("Bank", "http://Bank:3939", "https://some.host:3499/v1.0/invoke/Bank/method/")] + [InlineData("invalid", "http://Bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData(null, "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] + [InlineData("app-id.with.dots", "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] + [InlineData("invalid", "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] + [InlineData(null, "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] + [InlineData("App-id.with.dots", "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/App-id.with.dots/method/")] + [InlineData("invalid", "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")] + [InlineData(null, "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("bank", "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("invalid", "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData(null, "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData("Bank", "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/Bank/method/")] + [InlineData("invalid", "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")] + [InlineData(null, "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] + [InlineData("bank", "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] + [InlineData("invalid", "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] + [InlineData(null, "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] + [InlineData("Bank", "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/Bank/method/some/path")] + [InlineData("invalid", "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")] + [InlineData(null, "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] + [InlineData("bank", "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] + [InlineData("invalid", "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] + [InlineData(null, "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] + [InlineData("Bank", "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/Bank/method/some/path?q=test&p=another#fragment")] + [InlineData("invalid", "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")] + public void TryRewriteUri_WithNoAppId_RewritesUriToDaprInvoke(string? appId, string uri, string expected) + { + var handler = new InvocationHandler() { - var handler = new InvocationHandler() - { - DaprEndpoint = "https://some.host:3499", - DefaultAppId = appId, - }; + DaprEndpoint = "https://some.host:3499", + DefaultAppId = appId, + }; - Assert.True(handler.TryRewriteUri(new Uri(uri), out var rewritten)); - Assert.Equal(expected, rewritten!.OriginalString); - } + Assert.True(handler.TryRewriteUri(new Uri(uri), out var rewritten)); + Assert.Equal(expected, rewritten!.OriginalString); + } - [Fact] - public async Task SendAsync_InvalidNotSetUri_ThrowsException() + [Fact] + public async Task SendAsync_InvalidNotSetUri_ThrowsException() + { + var handler = new InvocationHandler(); + var ex = await Assert.ThrowsAsync(async () => { - var handler = new InvocationHandler(); - var ex = await Assert.ThrowsAsync(async () => - { - await CallSendAsync(handler, new HttpRequestMessage() { }); // No URI set - }); + await CallSendAsync(handler, new HttpRequestMessage() { }); // No URI set + }); - Assert.Contains("The request URI '' is not a valid Dapr service invocation destination.", ex.Message); - } + Assert.Contains("The request URI '' is not a valid Dapr service invocation destination.", ex.Message); + } - [Fact] - public async Task SendAsync_RewritesUri() + [Fact] + public async Task SendAsync_RewritesUri() + { + var uri = "http://bank/accounts/17?"; + + var capture = new CaptureHandler(); + var handler = new InvocationHandler() { - var uri = "http://bank/accounts/17?"; + InnerHandler = capture, - var capture = new CaptureHandler(); - var handler = new InvocationHandler() - { - InnerHandler = capture, + DaprEndpoint = "https://localhost:5000", + DaprApiToken = null, + }; - DaprEndpoint = "https://localhost:5000", - DaprApiToken = null, - }; + var request = new HttpRequestMessage(HttpMethod.Post, uri); + await CallSendAsync(handler, request); - var request = new HttpRequestMessage(HttpMethod.Post, uri); - await CallSendAsync(handler, request); + Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString); + Assert.Null(capture.DaprApiToken); - Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString); - Assert.Null(capture.DaprApiToken); + Assert.Equal(uri, request.RequestUri?.OriginalString); + Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); + } - Assert.Equal(uri, request.RequestUri?.OriginalString); - Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); - } + [Fact] + public async Task SendAsync_RewritesUri_AndAppId() + { + var uri = "http://bank/accounts/17?"; - [Fact] - public async Task SendAsync_RewritesUri_AndAppId() + var capture = new CaptureHandler(); + var handler = new InvocationHandler() { - var uri = "http://bank/accounts/17?"; + InnerHandler = capture, - var capture = new CaptureHandler(); - var handler = new InvocationHandler() - { - InnerHandler = capture, + DaprEndpoint = "https://localhost:5000", + DaprApiToken = null, + DefaultAppId = "Bank" + }; - DaprEndpoint = "https://localhost:5000", - DaprApiToken = null, - DefaultAppId = "Bank" - }; + var request = new HttpRequestMessage(HttpMethod.Post, uri); + await CallSendAsync(handler, request); - var request = new HttpRequestMessage(HttpMethod.Post, uri); - await CallSendAsync(handler, request); + Assert.Equal("https://localhost:5000/v1.0/invoke/Bank/method/accounts/17?", capture.RequestUri?.OriginalString); + Assert.Null(capture.DaprApiToken); - Assert.Equal("https://localhost:5000/v1.0/invoke/Bank/method/accounts/17?", capture.RequestUri?.OriginalString); - Assert.Null(capture.DaprApiToken); + Assert.Equal(uri, request.RequestUri?.OriginalString); + Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); + } - Assert.Equal(uri, request.RequestUri?.OriginalString); - Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); - } + [Fact] + public async Task SendAsync_RewritesUri_AndAddsApiToken() + { + var uri = "http://bank/accounts/17?"; - [Fact] - public async Task SendAsync_RewritesUri_AndAddsApiToken() + var capture = new CaptureHandler(); + var handler = new InvocationHandler() { - var uri = "http://bank/accounts/17?"; + InnerHandler = capture, - var capture = new CaptureHandler(); - var handler = new InvocationHandler() - { - InnerHandler = capture, + DaprEndpoint = "https://localhost:5000", + DaprApiToken = "super-duper-secure", + }; - DaprEndpoint = "https://localhost:5000", - DaprApiToken = "super-duper-secure", - }; + var request = new HttpRequestMessage(HttpMethod.Post, uri); + await CallSendAsync(handler, request); - var request = new HttpRequestMessage(HttpMethod.Post, uri); - await CallSendAsync(handler, request); + Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString); + Assert.Equal("super-duper-secure", capture.DaprApiToken); - Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString); - Assert.Equal("super-duper-secure", capture.DaprApiToken); + Assert.Equal(uri, request.RequestUri?.OriginalString); + Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); + } - Assert.Equal(uri, request.RequestUri?.OriginalString); - Assert.False(request.Headers.TryGetValues("dapr-api-token", out _)); - } + private async Task CallSendAsync(InvocationHandler handler, HttpRequestMessage message, CancellationToken cancellationToken = default) + { + // SendAsync is protected, can't call it directly. + var method = handler.GetType().GetMethod("SendAsync", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(method); - private async Task CallSendAsync(InvocationHandler handler, HttpRequestMessage message, CancellationToken cancellationToken = default) + try { - // SendAsync is protected, can't call it directly. - var method = handler.GetType().GetMethod("SendAsync", BindingFlags.Instance | BindingFlags.NonPublic); - Assert.NotNull(method); - - try - { - return await (Task)method!.Invoke(handler, new object[] { message, cancellationToken, })!; - } - catch (TargetInvocationException tie) // reflection always adds an extra layer of exceptions. - { - throw tie.InnerException!; - } + return await (Task)method!.Invoke(handler, new object[] { message, cancellationToken, })!; } - - private class CaptureHandler : HttpMessageHandler + catch (TargetInvocationException tie) // reflection always adds an extra layer of exceptions. { - public Uri? RequestUri { get; private set; } + throw tie.InnerException!; + } + } - public string? DaprApiToken { get; private set; } + private class CaptureHandler : HttpMessageHandler + { + public Uri? RequestUri { get; private set; } - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - RequestUri = request.RequestUri; - if (request.Headers.TryGetValues("dapr-api-token", out var tokens)) - { - DaprApiToken = tokens.SingleOrDefault(); - } + public string? DaprApiToken { get; private set; } - return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + RequestUri = request.RequestUri; + if (request.Headers.TryGetValues("dapr-api-token", out var tokens)) + { + DaprApiToken = tokens.SingleOrDefault(); } + + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); } } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/InvokeBindingApiTest.cs b/test/Dapr.Client.Test/InvokeBindingApiTest.cs index c980e889f..d721045d3 100644 --- a/test/Dapr.Client.Test/InvokeBindingApiTest.cs +++ b/test/Dapr.Client.Test/InvokeBindingApiTest.cs @@ -11,302 +11,301 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Client.Autogen.Grpc.v1; +using Shouldly; +using Google.Protobuf; +using Grpc.Core; +using Moq; +using Xunit; + +public class InvokeBindingApiTest { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Client.Autogen.Grpc.v1; - using Shouldly; - using Google.Protobuf; - using Grpc.Core; - using Moq; - using Xunit; - - public class InvokeBindingApiTest + [Fact] + public async Task InvokeBindingAsync_ValidateRequest() { - [Fact] - public async Task InvokeBindingAsync_ValidateRequest() + await using var client = TestClient.CreateForDaprClient(); + + var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.InvokeBindingAsync("test", "create", invokeRequest); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(0); + var json = envelope.Data.ToStringUtf8(); + var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); + typeFromRequest.RequestParameter.ShouldBe("Hello "); + } - var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.InvokeBindingAsync("test", "create", invokeRequest); - }); + [Fact] + public async Task InvokeBindingAsync_ValidateRequest_WithMetadata() + { + await using var client = TestClient.CreateForDaprClient(); - request.Dismiss(); + var metadata = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.InvokeBindingAsync("test", "create", invokeRequest, metadata); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + var json = envelope.Data.ToStringUtf8(); + var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); + typeFromRequest.RequestParameter.ShouldBe("Hello "); + } - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.ShouldBe("test"); - envelope.Metadata.Count.ShouldBe(0); - var json = envelope.Data.ToStringUtf8(); - var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.ShouldBe("Hello "); - } + [Fact] + public async Task InvokeBindingAsync_WithNullPayload_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task InvokeBindingAsync_ValidateRequest_WithMetadata() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.InvokeBindingAsync("test", "create", null); + }); + + request.Dismiss(); - var metadata = new Dictionary + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(0); + var json = envelope.Data.ToStringUtf8(); + Assert.Equal("null", json); + } + + [Fact] + public async Task InvokeBindingAsync_WithRequest_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + + var payload = new InvokeRequest() { RequestParameter = "Hello " }; + var bindingRequest = new BindingRequest("test", "create") + { + Data = JsonSerializer.SerializeToUtf8Bytes(payload, client.InnerClient.JsonSerializerOptions), + Metadata = { { "key1", "value1" }, { "key2", "value2" } - }; - var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.InvokeBindingAsync("test", "create", invokeRequest, metadata); - }); + } + }; - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.ShouldBe("test"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - var json = envelope.Data.ToStringUtf8(); - var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.ShouldBe("Hello "); - } - - [Fact] - public async Task InvokeBindingAsync_WithNullPayload_ValidateRequest() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + return await daprClient.InvokeBindingAsync(bindingRequest); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => + var gRpcResponse = new Autogen.Grpc.v1.InvokeBindingResponse() + { + Data = ByteString.CopyFrom(JsonSerializer.SerializeToUtf8Bytes(new Widget() { Color = "red", }, client.InnerClient.JsonSerializerOptions)), + Metadata = { - await daprClient.InvokeBindingAsync("test", "create", null); + { "anotherkey", "anothervalue" }, + } + }; + var response = await request.CompleteWithMessageAsync(gRpcResponse); + + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + + var json = envelope.Data.ToStringUtf8(); + var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); + typeFromRequest.RequestParameter.ShouldBe("Hello "); + + Assert.Same(bindingRequest, response.Request); + Assert.Equal("red", JsonSerializer.Deserialize(response.Data.Span, client.InnerClient.JsonSerializerOptions).Color); + Assert.Collection( + response.Metadata, + kvp => + { + Assert.Equal("anotherkey", kvp.Key); + Assert.Equal("anothervalue", kvp.Value); }); + } - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.ShouldBe("test"); - envelope.Metadata.Count.ShouldBe(0); - var json = envelope.Data.ToStringUtf8(); - Assert.Equal("null", json); - } - [Fact] - public async Task InvokeBindingAsync_WithRequest_ValidateRequest() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task InvokeBindingAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - var payload = new InvokeRequest() { RequestParameter = "Hello " }; - var bindingRequest = new BindingRequest("test", "create") - { - Data = JsonSerializer.SerializeToUtf8Bytes(payload, client.InnerClient.JsonSerializerOptions), - Metadata = - { - { "key1", "value1" }, - { "key2", "value2" } - } - }; - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeBindingAsync(bindingRequest); - }); + var cts = new CancellationTokenSource(); + cts.Cancel(); - var gRpcResponse = new Autogen.Grpc.v1.InvokeBindingResponse() - { - Data = ByteString.CopyFrom(JsonSerializer.SerializeToUtf8Bytes(new Widget() { Color = "red", }, client.InnerClient.JsonSerializerOptions)), - Metadata = - { - { "anotherkey", "anothervalue" }, - } - }; - var response = await request.CompleteWithMessageAsync(gRpcResponse); - - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.ShouldBe("test"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - - var json = envelope.Data.ToStringUtf8(); - var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.ShouldBe("Hello "); - - Assert.Same(bindingRequest, response.Request); - Assert.Equal("red", JsonSerializer.Deserialize(response.Data.Span, client.InnerClient.JsonSerializerOptions).Color); - Assert.Collection( - response.Metadata, - kvp => - { - Assert.Equal("anotherkey", kvp.Key); - Assert.Equal("anothervalue", kvp.Value); - }); - } - - - [Fact] - public async Task InvokeBindingAsync_WithCancelledToken() + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); + { "key1", "value1" }, + { "key2", "value2" } + }; + var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " }; + await Assert.ThrowsAsync(async () => + { + await client.InnerClient.InvokeBindingAsync("test", "create", invokeRequest, metadata, cts.Token); + }); + } - var cts = new CancellationTokenSource(); - cts.Cancel(); + [Fact] + public async Task InvokeBindingAsync_WrapsRpcException() + { + var client = new MockClient(); - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " }; - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.InvokeBindingAsync("test", "create", invokeRequest, metadata, cts.Token); - }); - } + var rpcStatus = new Status(StatusCode.Internal, "not gonna work"); + var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); - [Fact] - public async Task InvokeBindingAsync_WrapsRpcException() - { - var client = new MockClient(); + client.Mock + .Setup(m => m.InvokeBindingAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - var rpcStatus = new Status(StatusCode.Internal, "not gonna work"); - var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); + var ex = await Assert.ThrowsAsync(async () => + { + await client.DaprClient.InvokeBindingAsync("test", "test", new InvokeRequest() { RequestParameter = "Hello " }); + }); + Assert.Same(rpcException, ex.InnerException); + } - client.Mock - .Setup(m => m.InvokeBindingAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + [Fact] + public async Task InvokeBindingAsync_WrapsJsonException() + { + await using var client = TestClient.CreateForDaprClient(); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.InvokeBindingAsync("test", "test", new InvokeRequest() { RequestParameter = "Hello " }); - }); - Assert.Same(rpcException, ex.InnerException); - } + var response = new Autogen.Grpc.v1.InvokeBindingResponse(); + var bytes = JsonSerializer.SerializeToUtf8Bytes(new Widget(){ Color = "red", }, client.InnerClient.JsonSerializerOptions); + response.Data = ByteString.CopyFrom(bytes.Take(10).ToArray()); // trim it to make invalid JSON blob - [Fact] - public async Task InvokeBindingAsync_WrapsJsonException() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + return await daprClient.InvokeBindingAsync("test", "test", new InvokeRequest() { RequestParameter = "Hello " }); + }); - var response = new Autogen.Grpc.v1.InvokeBindingResponse(); - var bytes = JsonSerializer.SerializeToUtf8Bytes(new Widget(){ Color = "red", }, client.InnerClient.JsonSerializerOptions); - response.Data = ByteString.CopyFrom(bytes.Take(10).ToArray()); // trim it to make invalid JSON blob + await request.GetRequestEnvelopeAsync(); + var ex = await Assert.ThrowsAsync(async () => + { + await request.CompleteWithMessageAsync(response); + }); + Assert.IsType(ex.InnerException); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeBindingAsync("test", "test", new InvokeRequest() { RequestParameter = "Hello " }); - }); + [Fact] + public async Task InvokeBindingRequest_WithCustomRequest_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); - await request.GetRequestEnvelopeAsync(); - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteWithMessageAsync(response); - }); - Assert.IsType(ex.InnerException); - } + var data = new InvokeRequest { RequestParameter = "Test" }; + var metadata = new Dictionary + { + { "key1", "value1" } + }; + var req = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.InvokeBindingAsync("binding", "operation", data, metadata); + }); - [Fact] - public async Task InvokeBindingRequest_WithCustomRequest_ValidateRequest() + var resp = new InvokeBindingResponse { - await using var client = TestClient.CreateForDaprClient(); + Data = ByteString.CopyFrom(JsonSerializer.SerializeToUtf8Bytes(new Widget() { Color = "red", }, client.InnerClient.JsonSerializerOptions)) + }; + var response = await req.CompleteWithMessageAsync(resp); + + var envelope = await req.GetRequestEnvelopeAsync(); + envelope.Name.ShouldBe("binding"); + envelope.Operation.ShouldBe("operation"); + envelope.Metadata.Count.ShouldBe(1); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + + var json = envelope.Data.ToStringUtf8(); + var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); + typeFromRequest.RequestParameter.ShouldBe("Test"); + + Assert.Equal("red", response.Color); + } - var data = new InvokeRequest { RequestParameter = "Test" }; - var metadata = new Dictionary - { - { "key1", "value1" } - }; - var req = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeBindingAsync("binding", "operation", data, metadata); - }); + [Fact] + public async Task InvokeBindingRequest_WithNullData_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); - var resp = new InvokeBindingResponse - { - Data = ByteString.CopyFrom(JsonSerializer.SerializeToUtf8Bytes(new Widget() { Color = "red", }, client.InnerClient.JsonSerializerOptions)) - }; - var response = await req.CompleteWithMessageAsync(resp); - - var envelope = await req.GetRequestEnvelopeAsync(); - envelope.Name.ShouldBe("binding"); - envelope.Operation.ShouldBe("operation"); - envelope.Metadata.Count.ShouldBe(1); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - - var json = envelope.Data.ToStringUtf8(); - var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.ShouldBe("Test"); - - Assert.Equal("red", response.Color); - } - - [Fact] - public async Task InvokeBindingRequest_WithNullData_ValidateRequest() + var req = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + return await daprClient.InvokeBindingAsync("binding", "operation", null); + }); - var req = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.InvokeBindingAsync("binding", "operation", null); - }); + var resp = new InvokeBindingResponse + { + Data = ByteString.CopyFrom(JsonSerializer.SerializeToUtf8Bytes(new Widget() { Color = "red", }, client.InnerClient.JsonSerializerOptions)) + }; + var response = await req.CompleteWithMessageAsync(resp); - var resp = new InvokeBindingResponse - { - Data = ByteString.CopyFrom(JsonSerializer.SerializeToUtf8Bytes(new Widget() { Color = "red", }, client.InnerClient.JsonSerializerOptions)) - }; - var response = await req.CompleteWithMessageAsync(resp); + var envelope = await req.GetRequestEnvelopeAsync(); + envelope.Name.ShouldBe("binding"); + envelope.Operation.ShouldBe("operation"); - var envelope = await req.GetRequestEnvelopeAsync(); - envelope.Name.ShouldBe("binding"); - envelope.Operation.ShouldBe("operation"); + Assert.Equal("red", response.Color); + } - Assert.Equal("red", response.Color); - } + [Fact] + public async Task InvokeBindingRequest_WithBindingNull_CheckException() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task InvokeBindingRequest_WithBindingNull_CheckException() + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await client.InnerClient.InvokeBindingAsync(null, "operation", null); + }); + Assert.IsType(ex); + } - var ex = await Assert.ThrowsAsync(async () => - { - await client.InnerClient.InvokeBindingAsync(null, "operation", null); - }); - Assert.IsType(ex); - } + [Fact] + public async Task InvokeBindingRequest_WithOperationNull_CheckException() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task InvokeBindingRequest_WithOperationNull_CheckException() + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); - - var ex = await Assert.ThrowsAsync(async () => - { - await client.InnerClient.InvokeBindingAsync("binding", null, null); - }); - Assert.IsType(ex); - } + await client.InnerClient.InvokeBindingAsync("binding", null, null); + }); + Assert.IsType(ex); + } - private class InvokeRequest - { - public string RequestParameter { get; set; } - } + private class InvokeRequest + { + public string RequestParameter { get; set; } + } - private class Widget - { - public string Color { get; set; } - } + private class Widget + { + public string Color { get; set; } } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/MockClient.cs b/test/Dapr.Client.Test/MockClient.cs index d00a0a690..1ccdf14c7 100644 --- a/test/Dapr.Client.Test/MockClient.cs +++ b/test/Dapr.Client.Test/MockClient.cs @@ -11,132 +11,131 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Client; + +using System; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Net.Client; +using Moq; + +public class MockClient { - using System; - using System.Net.Http; - using System.Text.Json; - using System.Threading.Tasks; - using Grpc.Core; - using Grpc.Net.Client; - using Moq; - - public class MockClient + public MockClient() + { + Mock = new Mock(MockBehavior.Strict); + DaprClient = new DaprClientGrpc(GrpcChannel.ForAddress("http://localhost"), Mock.Object, new HttpClient(), new Uri("http://localhost:3500"), new JsonSerializerOptions(), default); + } + + public Mock Mock { get; } + + public DaprClient DaprClient { get; } + + public InvokeApiCallBuilder Call() + { + return new InvokeApiCallBuilder(); + } + + public StateApiCallBuilder CallStateApi() { - public MockClient() + return new StateApiCallBuilder(); + } + + public class InvokeApiCallBuilder + { + private TResponse response; + private readonly Metadata headers; + private Status status; + private readonly Metadata trailers; + + public InvokeApiCallBuilder() { - Mock = new Mock(MockBehavior.Strict); - DaprClient = new DaprClientGrpc(GrpcChannel.ForAddress("http://localhost"), Mock.Object, new HttpClient(), new Uri("http://localhost:3500"), new JsonSerializerOptions(), default); + headers = new Metadata(); + trailers = new Metadata(); } - public Mock Mock { get; } + public AsyncUnaryCall Build() + { + return new AsyncUnaryCall( + Task.FromResult(response), + Task.FromResult(headers), + () => status, + () => trailers, + () => { }); + } - public DaprClient DaprClient { get; } + public InvokeApiCallBuilder SetResponse(TResponse response) + { + this.response = response; + return this; + } - public InvokeApiCallBuilder Call() + public InvokeApiCallBuilder SetStatus(Status status) { - return new InvokeApiCallBuilder(); + this.status = status; + return this; } - public StateApiCallBuilder CallStateApi() + public InvokeApiCallBuilder AddHeader(string key, string value) { - return new StateApiCallBuilder(); + this.headers.Add(key, value); + return this; } - - public class InvokeApiCallBuilder + + public InvokeApiCallBuilder AddTrailer(string key, string value) + { + this.trailers.Add(key, value); + return this; + } + } + + public class StateApiCallBuilder + { + private TResponse response; + private readonly Metadata headers; + private Status status; + private readonly Metadata trailers; + + public StateApiCallBuilder() + { + headers = new Metadata(); + trailers = new Metadata(); + } + + public AsyncUnaryCall Build() + { + return new AsyncUnaryCall( + Task.FromResult(response), + Task.FromResult(headers), + () => status, + () => trailers, + () => { }); + } + + public StateApiCallBuilder SetResponse(TResponse response) + { + this.response = response; + return this; + } + + public StateApiCallBuilder SetStatus(Status status) + { + this.status = status; + return this; + } + + public StateApiCallBuilder AddHeader(string key, string value) { - private TResponse response; - private readonly Metadata headers; - private Status status; - private readonly Metadata trailers; - - public InvokeApiCallBuilder() - { - headers = new Metadata(); - trailers = new Metadata(); - } - - public AsyncUnaryCall Build() - { - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(headers), - () => status, - () => trailers, - () => { }); - } - - public InvokeApiCallBuilder SetResponse(TResponse response) - { - this.response = response; - return this; - } - - public InvokeApiCallBuilder SetStatus(Status status) - { - this.status = status; - return this; - } - - public InvokeApiCallBuilder AddHeader(string key, string value) - { - this.headers.Add(key, value); - return this; - } - - public InvokeApiCallBuilder AddTrailer(string key, string value) - { - this.trailers.Add(key, value); - return this; - } + this.headers.Add(key, value); + return this; } - public class StateApiCallBuilder + public StateApiCallBuilder AddTrailer(string key, string value) { - private TResponse response; - private readonly Metadata headers; - private Status status; - private readonly Metadata trailers; - - public StateApiCallBuilder() - { - headers = new Metadata(); - trailers = new Metadata(); - } - - public AsyncUnaryCall Build() - { - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(headers), - () => status, - () => trailers, - () => { }); - } - - public StateApiCallBuilder SetResponse(TResponse response) - { - this.response = response; - return this; - } - - public StateApiCallBuilder SetStatus(Status status) - { - this.status = status; - return this; - } - - public StateApiCallBuilder AddHeader(string key, string value) - { - this.headers.Add(key, value); - return this; - } - - public StateApiCallBuilder AddTrailer(string key, string value) - { - this.trailers.Add(key, value); - return this; - } + this.trailers.Add(key, value); + return this; } } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/PublishEventApiTest.cs b/test/Dapr.Client.Test/PublishEventApiTest.cs index 0dac474a3..7130fa4de 100644 --- a/test/Dapr.Client.Test/PublishEventApiTest.cs +++ b/test/Dapr.Client.Test/PublishEventApiTest.cs @@ -16,300 +16,299 @@ using System.Text.Json.Serialization; using Grpc.Net.Client; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Client.Autogen.Grpc.v1; +using Shouldly; +using Grpc.Core; +using Moq; +using Xunit; + +public class PublishEventApiTest { - using System; - using System.Collections.Generic; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Client.Autogen.Grpc.v1; - using Shouldly; - using Grpc.Core; - using Moq; - using Xunit; - - public class PublishEventApiTest + const string TestPubsubName = "testpubsubname"; + + [Fact] + public async Task PublishEventAsync_CanPublishTopicWithData() { - const string TestPubsubName = "testpubsubname"; + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task PublishEventAsync_CanPublishTopicWithData() + var publishData = new PublishData() { PublishObjectParameter = "testparam" }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.PublishEventAsync(TestPubsubName, "test", publishData); + }); - var publishData = new PublishData() { PublishObjectParameter = "testparam" }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishEventAsync(TestPubsubName, "test", publishData); - }); + request.Dismiss(); - request.Dismiss(); + var envelope = await request.GetRequestEnvelopeAsync(); + var jsonFromRequest = envelope.Data.ToStringUtf8(); - var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); + envelope.DataContentType.ShouldBe("application/json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); + } - envelope.DataContentType.ShouldBe("application/json"); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe("test"); - jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.ShouldBe(0); - } + [Fact] + public async Task PublishEvent_ShouldRespectJsonStringEnumConverter() + { + //The following mimics how the TestClient is built, but adds the JsonStringEnumConverter to the serialization options + var handler = new TestClient.CapturingHandler(); + var httpClient = new HttpClient(handler); + var clientBuilder = new DaprClientBuilder() + .UseJsonSerializationOptions(new JsonSerializerOptions() + { + Converters = {new JsonStringEnumConverter(null, false)} + }) + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions() + { + HttpClient = httpClient, ThrowOperationCanceledOnCancellation = true + }); + var client = new TestClient(clientBuilder.Build(), handler); + + //Ensure that the JsonStringEnumConverter is registered + client.InnerClient.JsonSerializerOptions.Converters.Count.ShouldBe(1); + client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.ShouldMatch(nameof(JsonStringEnumConverter)); - [Fact] - public async Task PublishEvent_ShouldRespectJsonStringEnumConverter() + var publishData = new Widget {Size = "Large", Color = WidgetColor.Red}; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - //The following mimics how the TestClient is built, but adds the JsonStringEnumConverter to the serialization options - var handler = new TestClient.CapturingHandler(); - var httpClient = new HttpClient(handler); - var clientBuilder = new DaprClientBuilder() - .UseJsonSerializationOptions(new JsonSerializerOptions() - { - Converters = {new JsonStringEnumConverter(null, false)} - }) - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions() - { - HttpClient = httpClient, ThrowOperationCanceledOnCancellation = true - }); - var client = new TestClient(clientBuilder.Build(), handler); - - //Ensure that the JsonStringEnumConverter is registered - client.InnerClient.JsonSerializerOptions.Converters.Count.ShouldBe(1); - client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.ShouldMatch(nameof(JsonStringEnumConverter)); + await daprClient.PublishEventAsync(TestPubsubName, "test", publishData); + }); - var publishData = new Widget {Size = "Large", Color = WidgetColor.Red}; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishEventAsync(TestPubsubName, "test", publishData); - }); + request.Dismiss(); - request.Dismiss(); + var envelope = await request.GetRequestEnvelopeAsync(); + var jsonFromRequest = envelope.Data.ToStringUtf8(); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); + jsonFromRequest.ShouldMatch("{\"Size\":\"Large\",\"Color\":\"Red\"}"); + } - var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); - jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); - jsonFromRequest.ShouldMatch("{\"Size\":\"Large\",\"Color\":\"Red\"}"); - } + [Fact] + public async Task PublishEventAsync_CanPublishTopicWithData_WithMetadata() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task PublishEventAsync_CanPublishTopicWithData_WithMetadata() + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); + { "key1", "value1" }, + { "key2", "value2" } + }; - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; + var publishData = new PublishData() { PublishObjectParameter = "testparam" }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.PublishEventAsync(TestPubsubName, "test", publishData, metadata); + }); - var publishData = new PublishData() { PublishObjectParameter = "testparam" }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishEventAsync(TestPubsubName, "test", publishData, metadata); - }); + request.Dismiss(); - request.Dismiss(); + var envelope = await request.GetRequestEnvelopeAsync(); + var jsonFromRequest = envelope.Data.ToStringUtf8(); - var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); + envelope.DataContentType.ShouldBe("application/json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); - envelope.DataContentType.ShouldBe("application/json"); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe("test"); - jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + } - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - } + [Fact] + public async Task PublishEventAsync_CanPublishTopicWithNoContent() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task PublishEventAsync_CanPublishTopicWithNoContent() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.PublishEventAsync(TestPubsubName, "test"); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishEventAsync(TestPubsubName, "test"); - }); + request.Dismiss(); - request.Dismiss(); + var envelope = await request.GetRequestEnvelopeAsync(); - var envelope = await request.GetRequestEnvelopeAsync(); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + envelope.Data.Length.ShouldBe(0); + envelope.Metadata.Count.ShouldBe(0); + } - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe("test"); - envelope.Data.Length.ShouldBe(0); - envelope.Metadata.Count.ShouldBe(0); - } + [Fact] + public async Task PublishEventAsync_CanPublishTopicWithNoContent_WithMetadata() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task PublishEventAsync_CanPublishTopicWithNoContent_WithMetadata() + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); + { "key1", "value1" }, + { "key2", "value2" } + }; - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.PublishEventAsync(TestPubsubName, "test", metadata); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishEventAsync(TestPubsubName, "test", metadata); - }); + request.Dismiss(); - request.Dismiss(); + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + envelope.Data.Length.ShouldBe(0); - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe("test"); - envelope.Data.Length.ShouldBe(0); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + } - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - } + [Fact] + public async Task PublishEventAsync_CanPublishCloudEventWithData() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task PublishEventAsync_CanPublishCloudEventWithData() + var publishData = new PublishData() { PublishObjectParameter = "testparam" }; + var cloudEvent = new CloudEvent(publishData) + { + Source = new Uri("urn:testsource"), + Type = "testtype", + Subject = "testsubject", + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.PublishEventAsync>(TestPubsubName, "test", cloudEvent); + }); - var publishData = new PublishData() { PublishObjectParameter = "testparam" }; - var cloudEvent = new CloudEvent(publishData) - { - Source = new Uri("urn:testsource"), - Type = "testtype", - Subject = "testsubject", - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishEventAsync>(TestPubsubName, "test", cloudEvent); - }); + request.Dismiss(); - request.Dismiss(); + var envelope = await request.GetRequestEnvelopeAsync(); + var jsonFromRequest = envelope.Data.ToStringUtf8(); - var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); + envelope.DataContentType.ShouldBe("application/cloudevents+json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); + } - envelope.DataContentType.ShouldBe("application/cloudevents+json"); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe("test"); - jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.ShouldBe(0); - } + [Fact] + public async Task PublishEventAsync_CanPublishCloudEventWithNoContent() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task PublishEventAsync_CanPublishCloudEventWithNoContent() + var cloudEvent = new CloudEvent { - await using var client = TestClient.CreateForDaprClient(); - - var cloudEvent = new CloudEvent - { - Source = new Uri("urn:testsource"), - Type = "testtype", - Subject = "testsubject", - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishEventAsync(TestPubsubName, "test", cloudEvent); - }); - - request.Dismiss(); + Source = new Uri("urn:testsource"), + Type = "testtype", + Subject = "testsubject", + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.PublishEventAsync(TestPubsubName, "test", cloudEvent); + }); - var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); + request.Dismiss(); - envelope.DataContentType.ShouldBe("application/cloudevents+json"); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe("test"); - jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.ShouldBe(0); - } + var envelope = await request.GetRequestEnvelopeAsync(); + var jsonFromRequest = envelope.Data.ToStringUtf8(); - [Fact] - public async Task PublishEventAsync_WithCancelledToken() - { - await using var client = TestClient.CreateForDaprClient(); + envelope.DataContentType.ShouldBe("application/cloudevents+json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); + } - var cts = new CancellationTokenSource(); - cts.Cancel(); + [Fact] + public async Task PublishEventAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.PublishEventAsync(TestPubsubName, "test", cancellationToken: cts.Token); - }); - } + var cts = new CancellationTokenSource(); + cts.Cancel(); - // All overloads call through a common path that does exception handling. - [Fact] - public async Task PublishEventAsync_WrapsRpcException() + await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.InnerClient.PublishEventAsync(TestPubsubName, "test", cancellationToken: cts.Token); + }); + } - var rpcStatus = new Status(StatusCode.Internal, "not gonna work"); - var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); + // All overloads call through a common path that does exception handling. + [Fact] + public async Task PublishEventAsync_WrapsRpcException() + { + var client = new MockClient(); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.PublishEventAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + var rpcStatus = new Status(StatusCode.Internal, "not gonna work"); + var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.PublishEventAsync("test", "test"); - }); - Assert.Same(rpcException, ex.InnerException); - } + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.PublishEventAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - [Fact] - public async Task PublishEventAsync_CanPublishWithRawData() + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await client.DaprClient.PublishEventAsync("test", "test"); + }); + Assert.Same(rpcException, ex.InnerException); + } - var publishData = new PublishData() { PublishObjectParameter = "testparam" }; - var publishBytes = JsonSerializer.SerializeToUtf8Bytes(publishData); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.PublishByteEventAsync(TestPubsubName, "test", publishBytes.AsMemory()); - }); + [Fact] + public async Task PublishEventAsync_CanPublishWithRawData() + { + await using var client = TestClient.CreateForDaprClient(); - request.Dismiss(); + var publishData = new PublishData() { PublishObjectParameter = "testparam" }; + var publishBytes = JsonSerializer.SerializeToUtf8Bytes(publishData); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.PublishByteEventAsync(TestPubsubName, "test", publishBytes.AsMemory()); + }); - var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); + request.Dismiss(); - envelope.DataContentType.ShouldBe("application/json"); - envelope.PubsubName.ShouldBe(TestPubsubName); - envelope.Topic.ShouldBe("test"); - jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData)); - // The default serializer forces camel case, so this should be different from our serialization above. - jsonFromRequest.ShouldNotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.ShouldBe(0); - } + var envelope = await request.GetRequestEnvelopeAsync(); + var jsonFromRequest = envelope.Data.ToStringUtf8(); - private class PublishData - { - public string PublishObjectParameter { get; set; } - } + envelope.DataContentType.ShouldBe("application/json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData)); + // The default serializer forces camel case, so this should be different from our serialization above. + jsonFromRequest.ShouldNotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); + } - private class Widget - { - public string Size { get; set; } - public WidgetColor Color { get; set; } - } + private class PublishData + { + public string PublishObjectParameter { get; set; } + } - private enum WidgetColor - { - Red, - Green, - Yellow - } + private class Widget + { + public string Size { get; set; } + public WidgetColor Color { get; set; } + } + + private enum WidgetColor + { + Red, + Green, + Yellow } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/SecretApiTest.cs b/test/Dapr.Client.Test/SecretApiTest.cs index 4f7d8aa5c..79dac62c4 100644 --- a/test/Dapr.Client.Test/SecretApiTest.cs +++ b/test/Dapr.Client.Test/SecretApiTest.cs @@ -11,352 +11,351 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Shouldly; +using Grpc.Core; +using Moq; +using Xunit; +using Autogenerated = Dapr.Client.Autogen.Grpc.v1; + +public class SecretApiTest { - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Shouldly; - using Grpc.Core; - using Moq; - using Xunit; - using Autogenerated = Dapr.Client.Autogen.Grpc.v1; - - public class SecretApiTest + [Fact] + public async Task GetSecretAsync_ValidateRequest() { - [Fact] - public async Task GetSecretAsync_ValidateRequest() + await using var client = TestClient.CreateForDaprClient(); + + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); - - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetSecretAsync("testStore", "test_key", metadata); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test_key"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - } + { "key1", "value1" }, + { "key2", "value2" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.GetSecretAsync("testStore", "test_key", metadata); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + } - [Fact] - public async Task GetSecretAsync_ReturnSingleSecret() + [Fact] + public async Task GetSecretAsync_ReturnSingleSecret() + { + await using var client = TestClient.CreateForDaprClient(); + + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); - - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetSecretAsync("testStore", "test_key", metadata); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test_key"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - - // Create Response & Respond - var secrets = new Dictionary - { - { "redis_secret", "Guess_Redis" } - }; - var secretsResponse = await SendResponseWithSecrets(secrets, request); - - // Get response and validate - secretsResponse.Count.ShouldBe(1); - secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); - secretsResponse["redis_secret"].ShouldBe("Guess_Redis"); - } + { "key1", "value1" }, + { "key2", "value2" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.GetSecretAsync("testStore", "test_key", metadata); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + + // Create Response & Respond + var secrets = new Dictionary + { + { "redis_secret", "Guess_Redis" } + }; + var secretsResponse = await SendResponseWithSecrets(secrets, request); + + // Get response and validate + secretsResponse.Count.ShouldBe(1); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"].ShouldBe("Guess_Redis"); + } - [Fact] - public async Task GetSecretAsync_WithSlashesInName() + [Fact] + public async Task GetSecretAsync_WithSlashesInName() + { + await using var client = TestClient.CreateForDaprClient(); + + var request = await client.CaptureGrpcRequestAsync(async DaprClient => { - await using var client = TestClient.CreateForDaprClient(); + return await DaprClient.GetSecretAsync("testStore", "us-west-1/org/xpto/secretabc"); + }); - var request = await client.CaptureGrpcRequestAsync(async DaprClient => - { - return await DaprClient.GetSecretAsync("testStore", "us-west-1/org/xpto/secretabc"); - }); + request.Dismiss(); - request.Dismiss(); + //Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("us-west-1/org/xpto/secretabc"); - //Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("us-west-1/org/xpto/secretabc"); + var secrets = new Dictionary { { "us-west-1/org/xpto/secretabc", "abc123" } }; + var secretsResponse = await SendResponseWithSecrets(secrets, request); - var secrets = new Dictionary { { "us-west-1/org/xpto/secretabc", "abc123" } }; - var secretsResponse = await SendResponseWithSecrets(secrets, request); + //Get response and validate + secretsResponse.Count.ShouldBe(1); + secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").ShouldBeTrue(); + secretsResponse["us-west-1/org/xpto/secretabc"].ShouldBe("abc123"); + } - //Get response and validate - secretsResponse.Count.ShouldBe(1); - secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").ShouldBeTrue(); - secretsResponse["us-west-1/org/xpto/secretabc"].ShouldBe("abc123"); - } + [Fact] + public async Task GetSecretAsync_ReturnMultipleSecrets() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task GetSecretAsync_ReturnMultipleSecrets() + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); - - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetSecretAsync("testStore", "test_key", metadata); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test_key"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - - // Create Response & Respond - var secrets = new Dictionary - { - { "redis_secret", "Guess_Redis" }, - { "kafka_secret", "Guess_Kafka" } - }; - var secretsResponse = await SendResponseWithSecrets(secrets, request); - - // Get response and validate - secretsResponse.Count.ShouldBe(2); - secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); - secretsResponse["redis_secret"].ShouldBe("Guess_Redis"); - secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue(); - secretsResponse["kafka_secret"].ShouldBe("Guess_Kafka"); - } - - [Fact] - public async Task GetSecretAsync_WithCancelledToken() + { "key1", "value1" }, + { "key2", "value2" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.GetSecretAsync("testStore", "test_key", metadata); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + + // Create Response & Respond + var secrets = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); + { "redis_secret", "Guess_Redis" }, + { "kafka_secret", "Guess_Kafka" } + }; + var secretsResponse = await SendResponseWithSecrets(secrets, request); + + // Get response and validate + secretsResponse.Count.ShouldBe(2); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"].ShouldBe("Guess_Redis"); + secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue(); + secretsResponse["kafka_secret"].ShouldBe("Guess_Kafka"); + } - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; + [Fact] + public async Task GetSecretAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - var cts = new CancellationTokenSource(); - cts.Cancel(); + var metadata = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.GetSecretAsync("testStore", "test_key", metadata, cancellationToken: cts.Token); - }); - } + var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task GetSecretAsync_WrapsRpcException() + await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.InnerClient.GetSecretAsync("testStore", "test_key", metadata, cancellationToken: cts.Token); + }); + } - var rpcStatus = new Grpc.Core.Status(StatusCode.Internal, "not gonna work"); - var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); + [Fact] + public async Task GetSecretAsync_WrapsRpcException() + { + var client = new MockClient(); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.GetSecretAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + var rpcStatus = new Grpc.Core.Status(StatusCode.Internal, "not gonna work"); + var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.GetSecretAsync("test", "test"); - }); - Assert.Same(rpcException, ex.InnerException); - } + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.GetSecretAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - [Fact] - public async Task GetBulkSecretAsync_ValidateRequest() + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); - - var metadata = new Dictionary(); - metadata.Add("key1", "value1"); - metadata.Add("key2", "value2"); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetBulkSecretAsync("testStore", metadata); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - } + await client.DaprClient.GetSecretAsync("test", "test"); + }); + Assert.Same(rpcException, ex.InnerException); + } - [Fact] - public async Task GetBulkSecretAsync_ReturnSingleSecret() + [Fact] + public async Task GetBulkSecretAsync_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + + var metadata = new Dictionary(); + metadata.Add("key1", "value1"); + metadata.Add("key2", "value2"); + + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); - - var metadata = new Dictionary(); - metadata.Add("key1", "value1"); - metadata.Add("key2", "value2"); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetBulkSecretAsync("testStore", metadata); - }); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - - // Create Response & Respond - var secrets = new Dictionary(); - secrets.Add("redis_secret", "Guess_Redis"); - var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); - - // Get response and validate - secretsResponse.Count.ShouldBe(1); - secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); - secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis"); - } + return await daprClient.GetBulkSecretAsync("testStore", metadata); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + } + + [Fact] + public async Task GetBulkSecretAsync_ReturnSingleSecret() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task GetBulkSecretAsync_ReturnMultipleSecrets() + var metadata = new Dictionary(); + metadata.Add("key1", "value1"); + metadata.Add("key2", "value2"); + + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); - - var metadata = new Dictionary(); - metadata.Add("key1", "value1"); - metadata.Add("key2", "value2"); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.GetBulkSecretAsync("testStore", metadata); - }); - - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Metadata.Count.ShouldBe(2); - envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); - envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); - envelope.Metadata["key1"].ShouldBe("value1"); - envelope.Metadata["key2"].ShouldBe("value2"); - - // Create Response & Respond - var secrets = new Dictionary(); - secrets.Add("redis_secret", "Guess_Redis"); - secrets.Add("kafka_secret", "Guess_Kafka"); - var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); - - // Get response and validate - secretsResponse.Count.ShouldBe(2); - secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); - secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis"); - secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue(); - secretsResponse["kafka_secret"]["kafka_secret"].ShouldBe("Guess_Kafka"); - } + return await daprClient.GetBulkSecretAsync("testStore", metadata); + }); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + + // Create Response & Respond + var secrets = new Dictionary(); + secrets.Add("redis_secret", "Guess_Redis"); + var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); + + // Get response and validate + secretsResponse.Count.ShouldBe(1); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis"); + } + + [Fact] + public async Task GetBulkSecretAsync_ReturnMultipleSecrets() + { + await using var client = TestClient.CreateForDaprClient(); + + var metadata = new Dictionary(); + metadata.Add("key1", "value1"); + metadata.Add("key2", "value2"); - [Fact] - public async Task GetBulkSecretAsync_WithCancelledToken() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + return await daprClient.GetBulkSecretAsync("testStore", metadata); + }); + + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); + + // Create Response & Respond + var secrets = new Dictionary(); + secrets.Add("redis_secret", "Guess_Redis"); + secrets.Add("kafka_secret", "Guess_Kafka"); + var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); + + // Get response and validate + secretsResponse.Count.ShouldBe(2); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis"); + secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue(); + secretsResponse["kafka_secret"]["kafka_secret"].ShouldBe("Guess_Kafka"); + } - var metadata = new Dictionary(); - metadata.Add("key1", "value1"); - metadata.Add("key2", "value2"); + [Fact] + public async Task GetBulkSecretAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - var cts = new CancellationTokenSource(); - cts.Cancel(); + var metadata = new Dictionary(); + metadata.Add("key1", "value1"); + metadata.Add("key2", "value2"); - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.GetBulkSecretAsync("testStore", metadata, cancellationToken: cts.Token); - }); - } + var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task GetBulkSecretAsync_WrapsRpcException() + await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.InnerClient.GetBulkSecretAsync("testStore", metadata, cancellationToken: cts.Token); + }); + } - var rpcStatus = new Grpc.Core.Status(StatusCode.Internal, "not gonna work"); - var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); + [Fact] + public async Task GetBulkSecretAsync_WrapsRpcException() + { + var client = new MockClient(); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.GetBulkSecretAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + var rpcStatus = new Grpc.Core.Status(StatusCode.Internal, "not gonna work"); + var rpcException = new RpcException(rpcStatus, new Metadata(), "not gonna work"); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.GetBulkSecretAsync("test"); - }); - Assert.Same(rpcException, ex.InnerException); - } + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.GetBulkSecretAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - private async Task SendResponseWithSecrets(Dictionary secrets, TestClient.TestGrpcRequest request) + var ex = await Assert.ThrowsAsync(async () => { - var secretResponse = new Autogenerated.GetSecretResponse(); - secretResponse.Data.Add(secrets); + await client.DaprClient.GetBulkSecretAsync("test"); + }); + Assert.Same(rpcException, ex.InnerException); + } - return await request.CompleteWithMessageAsync(secretResponse); - } + private async Task SendResponseWithSecrets(Dictionary secrets, TestClient.TestGrpcRequest request) + { + var secretResponse = new Autogenerated.GetSecretResponse(); + secretResponse.Data.Add(secrets); + + return await request.CompleteWithMessageAsync(secretResponse); + } - private async Task SendBulkResponseWithSecrets(Dictionary secrets, TestClient.TestGrpcRequest request) + private async Task SendBulkResponseWithSecrets(Dictionary secrets, TestClient.TestGrpcRequest request) + { + var getBulkSecretResponse = new Autogenerated.GetBulkSecretResponse(); + foreach (var secret in secrets) { - var getBulkSecretResponse = new Autogenerated.GetBulkSecretResponse(); - foreach (var secret in secrets) - { - var secretsResponse = new Autogenerated.SecretResponse(); - secretsResponse.Secrets[secret.Key] = secret.Value; - getBulkSecretResponse.Data.Add(secret.Key, secretsResponse); - } - - return await request.CompleteWithMessageAsync(getBulkSecretResponse); + var secretsResponse = new Autogenerated.SecretResponse(); + secretsResponse.Secrets[secret.Key] = secret.Value; + getBulkSecretResponse.Data.Add(secret.Key, secretsResponse); } + + return await request.CompleteWithMessageAsync(getBulkSecretResponse); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/StateApiTest.cs b/test/Dapr.Client.Test/StateApiTest.cs index 43c7f408b..7fcc44a49 100644 --- a/test/Dapr.Client.Test/StateApiTest.cs +++ b/test/Dapr.Client.Test/StateApiTest.cs @@ -29,1470 +29,1468 @@ using System.Net.Http; using System.Text; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +public class StateApiTest { - public class StateApiTest + [Fact] + public async Task GetStateAsync_CanReadState() { - [Fact] - public async Task GetStateAsync_CanReadState() - { - await using var client = TestClient.CreateForDaprClient(); + await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test")); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test")); - request.Dismiss(); + request.Dismiss(); - // Create Response & Respond - var data = new Widget() { Size = "small", Color = "yellow", }; - var envelope = MakeGetStateResponse(data); - var state = await request.CompleteWithMessageAsync(envelope); + // Create Response & Respond + var data = new Widget() { Size = "small", Color = "yellow", }; + var envelope = MakeGetStateResponse(data); + var state = await request.CompleteWithMessageAsync(envelope); - // Get response and validate - state.Size.ShouldBe("small"); - state.Color.ShouldBe("yellow"); - } + // Get response and validate + state.Size.ShouldBe("small"); + state.Color.ShouldBe("yellow"); + } - [Fact] - public async Task GetBulkStateAsync_CanReadState() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task GetBulkStateAsync_CanReadState() + { + await using var client = TestClient.CreateForDaprClient(); - const string key = "test"; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() { key }, null)); + const string key = "test"; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() { key }, null)); - // Create Response & Respond - const string data = "value"; - var envelope = MakeGetBulkStateResponse(key, data); - var state = await request.CompleteWithMessageAsync(envelope); + // Create Response & Respond + const string data = "value"; + var envelope = MakeGetBulkStateResponse(key, data); + var state = await request.CompleteWithMessageAsync(envelope); - // Get response and validate - state.Count.ShouldBe(1); - } + // Get response and validate + state.Count.ShouldBe(1); + } - [Fact] - public async Task GetBulkStateAsync_CanReadDeserializedState() - { - await using var client = TestClient.CreateForDaprClient(); - - const string key = "test"; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() {key}, null)); - - // Create Response & Respond - const string size = "small"; - const string color = "yellow"; - var data = new Widget() {Size = size, Color = color}; - var envelope = MakeGetBulkStateResponse(key, data); - var state = await request.CompleteWithMessageAsync(envelope); - - // Get response and validate - state.Count.ShouldBe(1); - state[0].Value.Size.ShouldMatch(size); - state[0].Value.Color.ShouldMatch(color); - } + [Fact] + public async Task GetBulkStateAsync_CanReadDeserializedState() + { + await using var client = TestClient.CreateForDaprClient(); + + const string key = "test"; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() {key}, null)); + + // Create Response & Respond + const string size = "small"; + const string color = "yellow"; + var data = new Widget() {Size = size, Color = color}; + var envelope = MakeGetBulkStateResponse(key, data); + var state = await request.CompleteWithMessageAsync(envelope); + + // Get response and validate + state.Count.ShouldBe(1); + state[0].Value.Size.ShouldMatch(size); + state[0].Value.Color.ShouldMatch(color); + } - [Fact] - public async Task GetBulkStateAsync_WrapsRpcException() - { - await using var client = TestClient.CreateForDaprClient(); - - const string key = "test"; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() { key }, null)); + [Fact] + public async Task GetBulkStateAsync_WrapsRpcException() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); - } + const string key = "test"; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() { key }, null)); - [Fact] - public async Task GetBulkStateAsync_ValidateRequest() + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } - const string key = "test"; - var metadata = new Dictionary - { - { "partitionKey", "mypartition" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() { key }, null, metadata: metadata)); + [Fact] + public async Task GetBulkStateAsync_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); - request.Dismiss(); + const string key = "test"; + var metadata = new Dictionary + { + { "partitionKey", "mypartition" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetBulkStateAsync("testStore", new List() { key }, null, metadata: metadata)); - // Create Response & Validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Metadata.ShouldBe(metadata); - } + request.Dismiss(); - [Fact] - public async Task GetStateAndEtagAsync_CanReadState() - { - await using var client = TestClient.CreateForDaprClient(); + // Create Response & Validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.ShouldBe(metadata); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAndETagAsync("testStore", "test")); + [Fact] + public async Task GetStateAndEtagAsync_CanReadState() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var data = new Widget() { Size = "small", Color = "yellow", }; - var envelope = MakeGetStateResponse(data, "Test_Etag"); - var (state, etag) = await request.CompleteWithMessageAsync(envelope); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAndETagAsync("testStore", "test")); - // Get response and validate - state.Size.ShouldBe("small"); - state.Color.ShouldBe("yellow"); - etag.ShouldBe("Test_Etag"); - } + // Create Response & Respond + var data = new Widget() { Size = "small", Color = "yellow", }; + var envelope = MakeGetStateResponse(data, "Test_Etag"); + var (state, etag) = await request.CompleteWithMessageAsync(envelope); - [Fact] - public async Task GetStateAndETagAsync_WrapsRpcException() - { - await using var client = TestClient.CreateForDaprClient(); + // Get response and validate + state.Size.ShouldBe("small"); + state.Color.ShouldBe("yellow"); + etag.ShouldBe("Test_Etag"); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAndETagAsync("testStore", "test")); + [Fact] + public async Task GetStateAndETagAsync_WrapsRpcException() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); - } + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAndETagAsync("testStore", "test")); - [Fact] - public async Task GetStateAndETagAsync_WrapsJsonException() + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAndETagAsync("testStore", "test")); + [Fact] + public async Task GetStateAndETagAsync_WrapsJsonException() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var envelope = new Autogenerated.GetStateResponse() - { - // Totally NOT valid JSON - Data = ByteString.CopyFrom(0x5b, 0x7b, 0x5b, 0x7b), - }; - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteWithMessageAsync(envelope); - }); - Assert.IsType(ex.InnerException); - } + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAndETagAsync("testStore", "test")); - [Fact] - public async Task GetStateAsync_CanReadEmptyState_ReturnsDefault() + // Create Response & Respond + var envelope = new Autogenerated.GetStateResponse() { - await using var client = TestClient.CreateForDaprClient(); + // Totally NOT valid JSON + Data = ByteString.CopyFrom(0x5b, 0x7b, 0x5b, 0x7b), + }; + var ex = await Assert.ThrowsAsync(async () => + { + await request.CompleteWithMessageAsync(envelope); + }); + Assert.IsType(ex.InnerException); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test", ConsistencyMode.Eventual)); + [Fact] + public async Task GetStateAsync_CanReadEmptyState_ReturnsDefault() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var envelope = MakeGetStateResponse(null); - var state = await request.CompleteWithMessageAsync(envelope); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test", ConsistencyMode.Eventual)); - // Get response and validate - state.ShouldBeNull(); - } + // Create Response & Respond + var envelope = MakeGetStateResponse(null); + var state = await request.CompleteWithMessageAsync(envelope); - [Theory] - [InlineData(ConsistencyMode.Eventual, StateConsistency.ConsistencyEventual)] - [InlineData(ConsistencyMode.Strong, StateConsistency.ConsistencyStrong)] - public async Task GetStateAsync_ValidateRequest(ConsistencyMode consistencyMode, StateConsistency expectedConsistencyMode) - { - await using var client = TestClient.CreateForDaprClient(); + // Get response and validate + state.ShouldBeNull(); + } + + [Theory] + [InlineData(ConsistencyMode.Eventual, StateConsistency.ConsistencyEventual)] + [InlineData(ConsistencyMode.Strong, StateConsistency.ConsistencyStrong)] + public async Task GetStateAsync_ValidateRequest(ConsistencyMode consistencyMode, StateConsistency expectedConsistencyMode) + { + await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test", consistencyMode)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test", consistencyMode)); - // Get Request & Validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); - envelope.Consistency.ShouldBe(expectedConsistencyMode); + // Get Request & Validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Consistency.ShouldBe(expectedConsistencyMode); - // Create Response & Respond - var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(null)); + // Create Response & Respond + var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(null)); - // Get response and validate - state.ShouldBeNull(); - } + // Get response and validate + state.ShouldBeNull(); + } - [Fact] - public async Task GetStateAndEtagAsync_ValidateRequest() + [Fact] + public async Task GetStateAndEtagAsync_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + + var metadata = new Dictionary { - await using var client = TestClient.CreateForDaprClient(); + { "partitionKey", "mypartition" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test", metadata: metadata)); - var metadata = new Dictionary - { - { "partitionKey", "mypartition" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test", metadata: metadata)); + // Get Request & Validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Metadata.ShouldBe(metadata); - // Get Request & Validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); - envelope.Metadata.ShouldBe(metadata); + // Create Response & Respond + var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(null)); - // Create Response & Respond - var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(null)); + // Get response and validate + state.ShouldBeNull(); + } - // Get response and validate - state.ShouldBeNull(); - } + [Fact] + public async Task GetStateAsync_WrapsRpcException() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task GetStateAsync_WrapsRpcException() + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test")); + + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test")); + [Fact] + public async Task GetStateAsync_WrapsJsonException() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); - } + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test")); - [Fact] - public async Task GetStateAsync_WrapsJsonException() + // Create Response & Respond + var stateResponse = new Autogenerated.GetStateResponse() { - await using var client = TestClient.CreateForDaprClient(); + // Totally NOT valid JSON + Data = ByteString.CopyFrom(0x5b, 0x7b, 0x5b, 0x7b), + }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateAsync("testStore", "test")); - - // Create Response & Respond - var stateResponse = new Autogenerated.GetStateResponse() - { - // Totally NOT valid JSON - Data = ByteString.CopyFrom(0x5b, 0x7b, 0x5b, 0x7b), - }; + var ex = await Assert.ThrowsAsync(async () => + { + await request.CompleteWithMessageAsync(stateResponse); + }); + Assert.IsType(ex.InnerException); + } - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteWithMessageAsync(stateResponse); - }); - Assert.IsType(ex.InnerException); - } + [Fact] + public async Task SaveStateAsync_CanSaveState() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task SaveStateAsync_CanSaveState() + var widget = new Widget() { Size = "small", Color = "yellow", }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.SaveStateAsync("testStore", "test", widget); + }); - var widget = new Widget() { Size = "small", Color = "yellow", }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveStateAsync("testStore", "test", widget); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Key.ShouldBe("test"); - - var stateJson = state.Value.ToStringUtf8(); - var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.ShouldBe(widget.Size); - stateFromRequest.Color.ShouldBe(widget.Color); - } + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Key.ShouldBe("test"); + + var stateJson = state.Value.ToStringUtf8(); + var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); + stateFromRequest.Size.ShouldBe(widget.Size); + stateFromRequest.Color.ShouldBe(widget.Color); + } - [Fact] - public async Task SaveBulkStateAsync_ValidateRequest() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task SaveBulkStateAsync_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); - var stateItem1 = new SaveStateItem("testKey1", "testValue1", "testEtag1", - new StateOptions { Concurrency = ConcurrencyMode.LastWrite }, - new Dictionary {{ "partitionKey1", "mypartition1" } }); + var stateItem1 = new SaveStateItem("testKey1", "testValue1", "testEtag1", + new StateOptions { Concurrency = ConcurrencyMode.LastWrite }, + new Dictionary {{ "partitionKey1", "mypartition1" } }); - var stateItem2 = new SaveStateItem("testKey2", "testValue2", "testEtag2", - new StateOptions { Concurrency = ConcurrencyMode.LastWrite }, - new Dictionary {{ "partitionKey2", "mypartition2" } }); + var stateItem2 = new SaveStateItem("testKey2", "testValue2", "testEtag2", + new StateOptions { Concurrency = ConcurrencyMode.LastWrite }, + new Dictionary {{ "partitionKey2", "mypartition2" } }); - var stateItem3 = new SaveStateItem("testKey3", "testValue3", null, - new StateOptions { Concurrency = ConcurrencyMode.LastWrite }, - new Dictionary {{ "partitionKey3", "mypartition3" } }); + var stateItem3 = new SaveStateItem("testKey3", "testValue3", null, + new StateOptions { Concurrency = ConcurrencyMode.LastWrite }, + new Dictionary {{ "partitionKey3", "mypartition3" } }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveBulkStateAsync("testStore", - new List>() { stateItem1, stateItem2, stateItem3}); - }); + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.SaveBulkStateAsync("testStore", + new List>() { stateItem1, stateItem2, stateItem3}); + }); - request.Dismiss(); + request.Dismiss(); - // Create Response & Validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(3); + // Create Response & Validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(3); - envelope.States[0].Key.ShouldBe("testKey1"); - envelope.States[0].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1"))); - envelope.States[0].Metadata.ShouldContainKey("partitionKey1"); + envelope.States[0].Key.ShouldBe("testKey1"); + envelope.States[0].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1"))); + envelope.States[0].Metadata.ShouldContainKey("partitionKey1"); - envelope.States[1].Key.ShouldBe("testKey2"); - envelope.States[1].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2"))); - envelope.States[1].Metadata.ShouldContainKey("partitionKey2"); + envelope.States[1].Key.ShouldBe("testKey2"); + envelope.States[1].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2"))); + envelope.States[1].Metadata.ShouldContainKey("partitionKey2"); - envelope.States[2].Key.ShouldBe("testKey3"); - envelope.States[2].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3"))); - envelope.States[2].Metadata.ShouldContainKey("partitionKey3"); - } - - [Fact] - public async Task GetStateAsync_WithCancelledToken() - { - await using var client = TestClient.CreateForDaprClient(); + envelope.States[2].Key.ShouldBe("testKey3"); + envelope.States[2].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3"))); + envelope.States[2].Metadata.ShouldContainKey("partitionKey3"); + } - var cts = new CancellationTokenSource(); - cts.Cancel(); + [Fact] + public async Task GetStateAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.GetStateAsync("testStore", "test", cancellationToken: cts.Token); - }); - } + var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task SaveStateAsync_CanClearState() + await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveStateAsync("testStore", "test", null); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); + await client.InnerClient.GetStateAsync("testStore", "test", cancellationToken: cts.Token); + }); + } - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Key.ShouldBe("test"); - state.Value.ShouldBe(ByteString.Empty); - } + [Fact] + public async Task SaveStateAsync_CanClearState() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task SaveStateAsync_WithCancelledToken() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.SaveStateAsync("testStore", "test", null); + }); - var cts = new CancellationTokenSource(); - cts.Cancel(); + request.Dismiss(); - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.SaveStateAsync("testStore", "test", null, cancellationToken: cts.Token); - }); - } + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); - [Fact] - public async Task SetStateAsync_ThrowsForNonSuccess() - { - await using var client = TestClient.CreateForDaprClient(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Key.ShouldBe("test"); + state.Value.ShouldBe(ByteString.Empty); + } - var widget = new Widget() { Size = "small", Color = "yellow", }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveStateAsync("testStore", "test", widget); - }); + [Fact] + public async Task SaveStateAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); - } + var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task ExecuteStateTransactionAsync_CanSaveState() + await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await client.InnerClient.SaveStateAsync("testStore", "test", null, cancellationToken: cts.Token); + }); + } - var stateValue1 = new Widget() { Size = "small", Color = "yellow", }; - var metadata1 = new Dictionary() - { - {"a", "b" } - }; - var options1 = new StateOptions - { - Concurrency = ConcurrencyMode.LastWrite - }; + [Fact] + public async Task SetStateAsync_ThrowsForNonSuccess() + { + await using var client = TestClient.CreateForDaprClient(); - var state1 = new StateTransactionRequest("stateKey1", JsonSerializer.SerializeToUtf8Bytes(stateValue1), StateOperationType.Upsert, "testEtag", metadata1, options1); - const int stateValue2 = 100; - var state2 = new StateTransactionRequest("stateKey2", JsonSerializer.SerializeToUtf8Bytes(stateValue2), StateOperationType.Delete); + var widget = new Widget() { Size = "small", Color = "yellow", }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.SaveStateAsync("testStore", "test", widget); + }); - const string stateValue3 = "teststring"; - var state3 = new StateTransactionRequest("stateKey3", JsonSerializer.SerializeToUtf8Bytes(stateValue3), StateOperationType.Upsert); + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => + { + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } - var states = new List - { - state1, - state2, - state3 - }; + [Fact] + public async Task ExecuteStateTransactionAsync_CanSaveState() + { + await using var client = TestClient.CreateForDaprClient(); + + var stateValue1 = new Widget() { Size = "small", Color = "yellow", }; + var metadata1 = new Dictionary() + { + {"a", "b" } + }; + var options1 = new StateOptions + { + Concurrency = ConcurrencyMode.LastWrite + }; + + var state1 = new StateTransactionRequest("stateKey1", JsonSerializer.SerializeToUtf8Bytes(stateValue1), StateOperationType.Upsert, "testEtag", metadata1, options1); + const int stateValue2 = 100; + var state2 = new StateTransactionRequest("stateKey2", JsonSerializer.SerializeToUtf8Bytes(stateValue2), StateOperationType.Delete); + + const string stateValue3 = "teststring"; + var state3 = new StateTransactionRequest("stateKey3", JsonSerializer.SerializeToUtf8Bytes(stateValue3), StateOperationType.Upsert); + + var states = new List + { + state1, + state2, + state3 + }; + + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.ExecuteStateTransactionAsync("testStore", states); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + + envelope.StoreName.ShouldBe("testStore"); + envelope.Operations.Count.ShouldBe(3); + + var req1 = envelope.Operations[0]; + req1.Request.Key.ShouldBe("stateKey1"); + req1.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower()); + var valueJson1 = req1.Request.Value.ToStringUtf8(); + var value1 = JsonSerializer.Deserialize(valueJson1, client.InnerClient.JsonSerializerOptions); + value1.Size.ShouldBe(stateValue1.Size); + value1.Color.ShouldBe(stateValue1.Color); + req1.Request.Etag.Value.ShouldBe("testEtag"); + req1.Request.Metadata.Count.ShouldBe(1); + req1.Request.Metadata["a"].ShouldBe("b"); + req1.Request.Options.Concurrency.ShouldBe(StateConcurrency.ConcurrencyLastWrite); + + var req2 = envelope.Operations[1]; + req2.Request.Key.ShouldBe("stateKey2"); + req2.OperationType.ShouldBe(StateOperationType.Delete.ToString().ToLower()); + var valueJson2 = req2.Request.Value.ToStringUtf8(); + var value2 = JsonSerializer.Deserialize(valueJson2, client.InnerClient.JsonSerializerOptions); + value2.ShouldBe(100); + + var req3 = envelope.Operations[2]; + req3.Request.Key.ShouldBe("stateKey3"); + req3.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower()); + var valueJson3 = req3.Request.Value.ToStringUtf8(); + var value3 = JsonSerializer.Deserialize(valueJson3, client.InnerClient.JsonSerializerOptions); + value3.ShouldBe("teststring"); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.ExecuteStateTransactionAsync("testStore", states); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - - envelope.StoreName.ShouldBe("testStore"); - envelope.Operations.Count.ShouldBe(3); - - var req1 = envelope.Operations[0]; - req1.Request.Key.ShouldBe("stateKey1"); - req1.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower()); - var valueJson1 = req1.Request.Value.ToStringUtf8(); - var value1 = JsonSerializer.Deserialize(valueJson1, client.InnerClient.JsonSerializerOptions); - value1.Size.ShouldBe(stateValue1.Size); - value1.Color.ShouldBe(stateValue1.Color); - req1.Request.Etag.Value.ShouldBe("testEtag"); - req1.Request.Metadata.Count.ShouldBe(1); - req1.Request.Metadata["a"].ShouldBe("b"); - req1.Request.Options.Concurrency.ShouldBe(StateConcurrency.ConcurrencyLastWrite); - - var req2 = envelope.Operations[1]; - req2.Request.Key.ShouldBe("stateKey2"); - req2.OperationType.ShouldBe(StateOperationType.Delete.ToString().ToLower()); - var valueJson2 = req2.Request.Value.ToStringUtf8(); - var value2 = JsonSerializer.Deserialize(valueJson2, client.InnerClient.JsonSerializerOptions); - value2.ShouldBe(100); - - var req3 = envelope.Operations[2]; - req3.Request.Key.ShouldBe("stateKey3"); - req3.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower()); - var valueJson3 = req3.Request.Value.ToStringUtf8(); - var value3 = JsonSerializer.Deserialize(valueJson3, client.InnerClient.JsonSerializerOptions); - value3.ShouldBe("teststring"); - } + [Fact] + public async Task ExecuteStateTransactionAsync_ThrowsForNonSuccess() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task ExecuteStateTransactionAsync_ThrowsForNonSuccess() + var widget1 = new Widget() { Size = "small", Color = "yellow", }; + var state1 = new StateTransactionRequest("stateKey1", JsonSerializer.SerializeToUtf8Bytes(widget1), StateOperationType.Upsert); + var states = new List { - await using var client = TestClient.CreateForDaprClient(); + state1 + }; - var widget1 = new Widget() { Size = "small", Color = "yellow", }; - var state1 = new StateTransactionRequest("stateKey1", JsonSerializer.SerializeToUtf8Bytes(widget1), StateOperationType.Upsert); - var states = new List - { - state1 - }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.ExecuteStateTransactionAsync("testStore", states); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.ExecuteStateTransactionAsync("testStore", states); - }); + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => + { + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); - } + [Fact] + public async Task ExecuteStateTransactionAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task ExecuteStateTransactionAsync_WithCancelledToken() + var operation = new StateTransactionRequest("test", null, StateOperationType.Delete); + var operations = new List { - await using var client = TestClient.CreateForDaprClient(); + operation, + }; - var operation = new StateTransactionRequest("test", null, StateOperationType.Delete); - var operations = new List - { - operation, - }; + var cts = new CancellationTokenSource(); + cts.Cancel(); - var cts = new CancellationTokenSource(); - cts.Cancel(); + await Assert.ThrowsAsync(async () => + { + await client.InnerClient.ExecuteStateTransactionAsync("testStore", operations, new Dictionary(), cancellationToken: cts.Token); + }); + } - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.ExecuteStateTransactionAsync("testStore", operations, new Dictionary(), cancellationToken: cts.Token); - }); - } + [Fact] + public async Task DeleteStateAsync_CanDeleteState() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task DeleteStateAsync_CanDeleteState() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.DeleteStateAsync("testStore", "test"); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.DeleteStateAsync("testStore", "test"); - }); + request.Dismiss(); - request.Dismiss(); + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + } - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); - } + [Fact] + public async Task DeleteStateAsync_ThrowsForNonSuccess() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task DeleteStateAsync_ThrowsForNonSuccess() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.DeleteStateAsync("testStore", "test"); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.DeleteStateAsync("testStore", "test"); - }); - - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); - } - - [Fact] - public async Task DeleteStateAsync_WithCancelledToken() + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } - var cts = new CancellationTokenSource(); - cts.Cancel(); + [Fact] + public async Task DeleteStateAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.DeleteStateAsync("testStore", "key", cancellationToken: cts.Token); - }); - } + var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task GetStateEntryAsync_CanReadState() + await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await client.InnerClient.DeleteStateAsync("testStore", "key", cancellationToken: cts.Token); + }); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); + [Fact] + public async Task GetStateEntryAsync_CanReadState() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var data = new Widget() { Size = "small", Color = "yellow", }; - var envelope = MakeGetStateResponse(data); - var state = await request.CompleteWithMessageAsync(envelope); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); - // Get response and validate - state.Value.Size.ShouldBe("small"); - state.Value.Color.ShouldBe("yellow"); - } + // Create Response & Respond + var data = new Widget() { Size = "small", Color = "yellow", }; + var envelope = MakeGetStateResponse(data); + var state = await request.CompleteWithMessageAsync(envelope); - [Fact] - public async Task GetStateEntryAsync_CanReadEmptyState_ReturnsDefault() - { - await using var client = TestClient.CreateForDaprClient(); + // Get response and validate + state.Value.Size.ShouldBe("small"); + state.Value.Color.ShouldBe("yellow"); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); + [Fact] + public async Task GetStateEntryAsync_CanReadEmptyState_ReturnsDefault() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var envelope = MakeGetStateResponse(null); - var state = await request.CompleteWithMessageAsync(envelope); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); - state.Key.ShouldBe("test"); - state.Value.ShouldBeNull(); - } + // Create Response & Respond + var envelope = MakeGetStateResponse(null); + var state = await request.CompleteWithMessageAsync(envelope); - [Fact] - public async Task GetStateEntryAsync_CanSaveState() - { - await using var client = TestClient.CreateForDaprClient(); + state.Key.ShouldBe("test"); + state.Value.ShouldBeNull(); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); + [Fact] + public async Task GetStateEntryAsync_CanSaveState() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var data = new Widget() { Size = "small", Color = "yellow", }; - var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); - state.Key.ShouldBe("test"); - state.Value.Size.ShouldBe("small"); - state.Value.Color.ShouldBe("yellow"); + // Create Response & Respond + var data = new Widget() { Size = "small", Color = "yellow", }; + var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); - // Modify the state and save it - state.Value.Color = "green"; + state.Key.ShouldBe("test"); + state.Value.Size.ShouldBe("small"); + state.Value.Color.ShouldBe("yellow"); - var request2 = await client.CaptureGrpcRequestAsync(async _ => - { - await state.SaveAsync(); - }); + // Modify the state and save it + state.Value.Color = "green"; - request2.Dismiss(); + var request2 = await client.CaptureGrpcRequestAsync(async _ => + { + await state.SaveAsync(); + }); - // Get Request and validate - var envelope = await request2.GetRequestEnvelopeAsync(); + request2.Dismiss(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var requestState = envelope.States[0]; - requestState.Key.ShouldBe("test"); + // Get Request and validate + var envelope = await request2.GetRequestEnvelopeAsync(); - var stateJson = requestState.Value.ToStringUtf8(); - var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.ShouldBe("small"); - stateFromRequest.Color.ShouldBe("green"); - } + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var requestState = envelope.States[0]; + requestState.Key.ShouldBe("test"); - [Fact] - public async Task GetStateEntryAsync_CanDeleteState() - { - await using var client = TestClient.CreateForDaprClient(); + var stateJson = requestState.Value.ToStringUtf8(); + var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); + stateFromRequest.Size.ShouldBe("small"); + stateFromRequest.Color.ShouldBe("green"); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); + [Fact] + public async Task GetStateEntryAsync_CanDeleteState() + { + await using var client = TestClient.CreateForDaprClient(); - // Create Response & Respond - var data = new Widget() { Size = "small", Color = "yellow", }; - var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetStateEntryAsync("testStore", "test")); - state.Key.ShouldBe("test"); - state.Value.Size.ShouldBe("small"); - state.Value.Color.ShouldBe("yellow"); + // Create Response & Respond + var data = new Widget() { Size = "small", Color = "yellow", }; + var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); - state.Value.Color = "green"; - var request2 = await client.CaptureGrpcRequestAsync(async daprClient => - { - await state.DeleteAsync(); - }); + state.Key.ShouldBe("test"); + state.Value.Size.ShouldBe("small"); + state.Value.Color.ShouldBe("yellow"); - request2.Dismiss(); + state.Value.Color = "green"; + var request2 = await client.CaptureGrpcRequestAsync(async daprClient => + { + await state.DeleteAsync(); + }); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); + request2.Dismiss(); - } + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); - [Theory] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] - public async Task SaveStateAsync_ValidateOptions( - ConsistencyMode consistencyMode, - ConcurrencyMode concurrencyMode, - StateConsistency expectedConsistency, - StateConcurrency expectedConcurrency) - { - await using var client = TestClient.CreateForDaprClient(); + } - var widget = new Widget() { Size = "small", Color = "yellow", }; - var stateOptions = new StateOptions - { - Concurrency = concurrencyMode, - Consistency = consistencyMode - }; + [Theory] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] + public async Task SaveStateAsync_ValidateOptions( + ConsistencyMode consistencyMode, + ConcurrencyMode concurrencyMode, + StateConsistency expectedConsistency, + StateConcurrency expectedConcurrency) + { + await using var client = TestClient.CreateForDaprClient(); + + var widget = new Widget() { Size = "small", Color = "yellow", }; + var stateOptions = new StateOptions + { + Concurrency = concurrencyMode, + Consistency = consistencyMode + }; + + var metadata = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.SaveStateAsync("testStore", "test", widget, stateOptions, metadata); + }); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Key.ShouldBe("test"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); + + var stateJson = state.Value.ToStringUtf8(); + var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); + stateFromRequest.Size.ShouldBe(widget.Size); + stateFromRequest.Color.ShouldBe(widget.Color); + } - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; + [Theory] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] + public async Task TrySaveStateAsync_ValidateOptions( + ConsistencyMode consistencyMode, + ConcurrencyMode concurrencyMode, + StateConsistency expectedConsistency, + StateConcurrency expectedConcurrency) + { + await using var client = TestClient.CreateForDaprClient(); + + var widget = new Widget() { Size = "small", Color = "yellow", }; + var stateOptions = new StateOptions + { + Concurrency = concurrencyMode, + Consistency = consistencyMode + }; + + var metadata = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.TrySaveStateAsync("testStore", "test", widget, "Test_Etag", stateOptions, metadata)); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Etag.Value.ShouldBe("Test_Etag"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); + + var stateJson = state.Value.ToStringUtf8(); + var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); + stateFromRequest.Size.ShouldBe(widget.Size); + stateFromRequest.Color.ShouldBe(widget.Color); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveStateAsync("testStore", "test", widget, stateOptions, metadata); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Key.ShouldBe("test"); - state.Metadata.Count.ShouldBe(2); - state.Metadata.Keys.Contains("key1").ShouldBeTrue(); - state.Metadata.Keys.Contains("key2").ShouldBeTrue(); - state.Metadata["key1"].ShouldBe("value1"); - state.Metadata["key2"].ShouldBe("value2"); - state.Options.Concurrency.ShouldBe(expectedConcurrency); - state.Options.Consistency.ShouldBe(expectedConsistency); - - var stateJson = state.Value.ToStringUtf8(); - var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.ShouldBe(widget.Size); - stateFromRequest.Color.ShouldBe(widget.Color); - } + [Fact] + public async Task TrySaveStateAsync_ValidateNonETagErrorThrowsException() + { + var client = new MockClient(); - [Theory] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] - public async Task TrySaveStateAsync_ValidateOptions( - ConsistencyMode consistencyMode, - ConcurrencyMode concurrencyMode, - StateConsistency expectedConsistency, - StateConcurrency expectedConcurrency) - { - await using var client = TestClient.CreateForDaprClient(); + await client.CallStateApi().Build(); - var widget = new Widget() { Size = "small", Color = "yellow", }; - var stateOptions = new StateOptions - { - Concurrency = concurrencyMode, - Consistency = consistencyMode - }; + var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.TrySaveStateAsync("testStore", "test", widget, "Test_Etag", stateOptions, metadata)); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Etag.Value.ShouldBe("Test_Etag"); - state.Metadata.Count.ShouldBe(2); - state.Metadata.Keys.Contains("key1").ShouldBeTrue(); - state.Metadata.Keys.Contains("key2").ShouldBeTrue(); - state.Metadata["key1"].ShouldBe("value1"); - state.Metadata["key2"].ShouldBe("value2"); - state.Options.Concurrency.ShouldBe(expectedConcurrency); - state.Options.Consistency.ShouldBe(expectedConsistency); - - var stateJson = state.Value.ToStringUtf8(); - var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.ShouldBe(widget.Size); - stateFromRequest.Color.ShouldBe(widget.Color); - } + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - [Fact] - public async Task TrySaveStateAsync_ValidateNonETagErrorThrowsException() + var ex = await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", "someETag"); + }); + Assert.Same(rpcException, ex.InnerException); + } - await client.CallStateApi().Build(); + [Fact] + public async Task TrySaveStateAsync_ValidateETagRelatedExceptionReturnsFalse() + { + var client = new MockClient(); - var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); + await client.CallStateApi() + .Build(); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed saving state in state store testStore")); + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", "someETag"); - }); - Assert.Same(rpcException, ex.InnerException); - } + var operationResult = await client.DaprClient.TrySaveStateAsync("testStore", "test", "testValue", "invalidETag"); + Assert.False(operationResult); + } - [Fact] - public async Task TrySaveStateAsync_ValidateETagRelatedExceptionReturnsFalse() - { - var client = new MockClient(); + [Fact] + public async Task TrySaveStateAsync_NullEtagThrowsArgumentException() + { + var client = new MockClient(); - await client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); - var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed saving state in state store testStore")); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + await Should.ThrowAsync(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null)); + } - var operationResult = await client.DaprClient.TrySaveStateAsync("testStore", "test", "testValue", "invalidETag"); - Assert.False(operationResult); - } + [Fact] + public async Task TrySaveStateAsync_EmptyEtagDoesNotThrow() + { + var client = new MockClient(); + var response = client.CallStateApi() + .Build(); - [Fact] - public async Task TrySaveStateAsync_NullEtagThrowsArgumentException() - { - var client = new MockClient(); + // Setup the mock client to return success + client.Mock + .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) + .Returns(response); - await client.CallStateApi() - .Build(); + var result = await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", ""); + Assert.True(result); + } - await Should.ThrowAsync(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null)); - } + [Fact] + public async Task TryDeleteStateAsync_ValidateNonETagErrorThrowsException() + { + var client = new MockClient(); - [Fact] - public async Task TrySaveStateAsync_EmptyEtagDoesNotThrow() - { - var client = new MockClient(); - var response = client.CallStateApi() + await client.CallStateApi() .Build(); - // Setup the mock client to return success - client.Mock - .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) - .Returns(response); + var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); - var result = await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", ""); - Assert.True(result); - } + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.DeleteStateAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - [Fact] - public async Task TryDeleteStateAsync_ValidateNonETagErrorThrowsException() + var ex = await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.DaprClient.TryDeleteStateAsync("test", "test", "badEtag"); + }); + Assert.Same(rpcException, ex.InnerException); + } - await client.CallStateApi() - .Build(); + [Fact] + public async Task TryDeleteStateAsync_NullEtagThrowsArgumentException() + { + var client = new MockClient(); - var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); + await client.CallStateApi() + .Build(); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.DeleteStateAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + await Should.ThrowAsync(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null)); + } - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.TryDeleteStateAsync("test", "test", "badEtag"); - }); - Assert.Same(rpcException, ex.InnerException); - } + [Fact] + public async Task TryDeleteStateAsync_EmptyEtagDoesNotThrow() + { + var client = new MockClient(); + var response = client.CallStateApi() + .Build(); - [Fact] - public async Task TryDeleteStateAsync_NullEtagThrowsArgumentException() - { - var client = new MockClient(); + // Setup the mock client to return success + client.Mock + .Setup(m => m.DeleteStateAsync(It.IsAny(), It.IsAny())) + .Returns(response); - await client.CallStateApi() - .Build(); + var result = await client.DaprClient.TryDeleteStateAsync("test", "test", ""); + Assert.True(result); + } - await Should.ThrowAsync(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null)); - } + [Fact] + public async Task TryDeleteStateAsync_ValidateETagRelatedExceptionReturnsFalse() + { + var client = new MockClient(); - [Fact] - public async Task TryDeleteStateAsync_EmptyEtagDoesNotThrow() - { - var client = new MockClient(); - var response = client.CallStateApi() + await client.CallStateApi() .Build(); - // Setup the mock client to return success - client.Mock - .Setup(m => m.DeleteStateAsync(It.IsAny(), It.IsAny())) - .Returns(response); + var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed deleting state with key test")); + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.DeleteStateAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); - var result = await client.DaprClient.TryDeleteStateAsync("test", "test", ""); - Assert.True(result); - } - - [Fact] - public async Task TryDeleteStateAsync_ValidateETagRelatedExceptionReturnsFalse() - { - var client = new MockClient(); - - await client.CallStateApi() - .Build(); - - var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed deleting state with key test")); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.DeleteStateAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + var operationResult = await client.DaprClient.TryDeleteStateAsync("test", "test", "invalidETag"); + Assert.False(operationResult); + } - var operationResult = await client.DaprClient.TryDeleteStateAsync("test", "test", "invalidETag"); - Assert.False(operationResult); - } + [Theory] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] + public async Task DeleteStateAsync_ValidateOptions( + ConsistencyMode consistencyMode, + ConcurrencyMode concurrencyMode, + StateConsistency expectedConsistency, + StateConcurrency expectedConcurrency) + { + await using var client = TestClient.CreateForDaprClient(); - [Theory] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] - public async Task DeleteStateAsync_ValidateOptions( - ConsistencyMode consistencyMode, - ConcurrencyMode concurrencyMode, - StateConsistency expectedConsistency, - StateConcurrency expectedConcurrency) + var stateOptions = new StateOptions { - await using var client = TestClient.CreateForDaprClient(); + Concurrency = concurrencyMode, + Consistency = consistencyMode + }; - var stateOptions = new StateOptions - { - Concurrency = concurrencyMode, - Consistency = consistencyMode - }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.DeleteStateAsync("testStore", "test", stateOptions); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.DeleteStateAsync("testStore", "test", stateOptions); - }); + request.Dismiss(); - request.Dismiss(); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Options.Concurrency.ShouldBe(expectedConcurrency); + envelope.Options.Consistency.ShouldBe(expectedConsistency); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); - envelope.Options.Concurrency.ShouldBe(expectedConcurrency); - envelope.Options.Consistency.ShouldBe(expectedConsistency); + } - } + [Theory] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] + public async Task TryDeleteStateAsync_ValidateOptions( + ConsistencyMode consistencyMode, + ConcurrencyMode concurrencyMode, + StateConsistency expectedConsistency, + StateConcurrency expectedConcurrency) + { + await using var client = TestClient.CreateForDaprClient(); - [Theory] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] - public async Task TryDeleteStateAsync_ValidateOptions( - ConsistencyMode consistencyMode, - ConcurrencyMode concurrencyMode, - StateConsistency expectedConsistency, - StateConcurrency expectedConcurrency) + var stateOptions = new StateOptions { - await using var client = TestClient.CreateForDaprClient(); + Concurrency = concurrencyMode, + Consistency = consistencyMode + }; - var stateOptions = new StateOptions - { - Concurrency = concurrencyMode, - Consistency = consistencyMode - }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.TryDeleteStateAsync("testStore", "test", "Test_Etag", stateOptions)); - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.TryDeleteStateAsync("testStore", "test", "Test_Etag", stateOptions)); + request.Dismiss(); - request.Dismiss(); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Etag.Value.ShouldBe("Test_Etag"); + envelope.Options.Concurrency.ShouldBe(expectedConcurrency); + envelope.Options.Consistency.ShouldBe(expectedConsistency); + } - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); - envelope.Etag.Value.ShouldBe("Test_Etag"); - envelope.Options.Concurrency.ShouldBe(expectedConcurrency); - envelope.Options.Consistency.ShouldBe(expectedConsistency); - } + [Fact] + public async Task DeleteBulkStateAsync_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task DeleteBulkStateAsync_ValidateRequest() + const string key = "test"; + const string etag = "etag"; + var metadata = new Dictionary + { + { "partitionKey", "mypartition" } + }; + var deleteBulkStateItem = new BulkDeleteStateItem(key, etag, null, metadata); + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.DeleteBulkStateAsync("testStore", new List() { deleteBulkStateItem }); + }); - const string key = "test"; - const string etag = "etag"; - var metadata = new Dictionary - { - { "partitionKey", "mypartition" } - }; - var deleteBulkStateItem = new BulkDeleteStateItem(key, etag, null, metadata); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.DeleteBulkStateAsync("testStore", new List() { deleteBulkStateItem }); - }); + request.Dismiss(); - request.Dismiss(); + // Create Response & Validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + envelope.States[0].Key.ShouldBe(key); + envelope.States[0].Metadata.ShouldContainKey("partitionKey"); + } - // Create Response & Validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - envelope.States[0].Key.ShouldBe(key); - envelope.States[0].Metadata.ShouldContainKey("partitionKey"); - } + [Fact] + public async Task QueryStateAsync_ValidateResult() + { + await using var client = TestClient.CreateForDaprClient(); + + const string queryJson = "{'query':{'filter':{ 'EQ': {'value':'test'}}}}"; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.QueryStateAsync("testStore", queryJson, new Dictionary())); + + // Validate request. + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Query.ShouldBe(queryJson); + envelope.Metadata.ShouldBeEmpty(); + + // Validate response. + var testData = new Widget() { Color = "Green", Size = "Small" }; + var wireResponse = new Autogenerated.QueryStateResponse(); + wireResponse.Results.Add(MakeQueryStateItem("test", testData, "an etag")); + + var response = await request.CompleteWithMessageAsync(wireResponse); + response.Results.Count.ShouldBe(1); + response.Results[0].Key.ShouldBe("test"); + response.Results[0].Data.ShouldBe(testData); + response.Results[0].ETag.ShouldBe("an etag"); + response.Results[0].Error.ShouldBeNullOrEmpty(); + } - [Fact] - public async Task QueryStateAsync_ValidateResult() - { - await using var client = TestClient.CreateForDaprClient(); - - const string queryJson = "{'query':{'filter':{ 'EQ': {'value':'test'}}}}"; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.QueryStateAsync("testStore", queryJson, new Dictionary())); - - // Validate request. - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Query.ShouldBe(queryJson); - envelope.Metadata.ShouldBeEmpty(); - - // Validate response. - var testData = new Widget() { Color = "Green", Size = "Small" }; - var wireResponse = new Autogenerated.QueryStateResponse(); - wireResponse.Results.Add(MakeQueryStateItem("test", testData, "an etag")); - - var response = await request.CompleteWithMessageAsync(wireResponse); - response.Results.Count.ShouldBe(1); - response.Results[0].Key.ShouldBe("test"); - response.Results[0].Data.ShouldBe(testData); - response.Results[0].ETag.ShouldBe("an etag"); - response.Results[0].Error.ShouldBeNullOrEmpty(); - } + [Fact] + public async Task QueryStateAsync_EncountersError_ValidatePartialResult() + { + await using var client = TestClient.CreateForDaprClient(); + + const string queryJson = "{'query':{'filter':{ 'EQ': {'value':'test'}}}}"; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.QueryStateAsync("testStore", queryJson, new Dictionary())); + + // Validate request. + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Query.ShouldBe(queryJson); + envelope.Metadata.ShouldBeEmpty(); + + // Validate response, we expect to only get the first object as the 2nd will present an error. + var testData1 = new Widget() { Color = "Green", Size = "Small" }; + var testData2 = new Widget() { Color = "Green", Size = "Medium" }; + var testData3 = new Widget() { Color = "Green", Size = "Large" }; + var wireResponse = new Autogenerated.QueryStateResponse(); + + wireResponse.Results.Add(MakeQueryStateItem("test1", testData1)); + wireResponse.Results.Add(MakeQueryStateItem("test2", testData2, string.Empty, "An error!")); + wireResponse.Results.Add(MakeQueryStateItem("test3", testData3)); + + var ex = await Assert.ThrowsAsync>(() => request.CompleteWithMessageAsync(wireResponse)); + ex.Message.ShouldBe("Encountered an error while processing state query results."); + var response = ex.Response; + response.Results.Count.ShouldBe(2); + response.Results[0].Key.ShouldBe("test1"); + response.Results[0].Data.ShouldBe(testData1); + response.Results[0].ETag.ShouldBeNullOrEmpty(); + response.Results[0].Error.ShouldBeNullOrEmpty(); + response.Results[1].Key.ShouldBe("test3"); + response.Results[1].Data.ShouldBe(testData3); + response.Results[1].ETag.ShouldBeNullOrEmpty(); + response.Results[1].Error.ShouldBeNullOrEmpty(); + + var failedKeys = ex.FailedKeys; + failedKeys.Count.ShouldBe(1); + failedKeys[0].ShouldBe("test2"); + } - [Fact] - public async Task QueryStateAsync_EncountersError_ValidatePartialResult() + private Autogenerated.GetStateResponse MakeGetStateResponse(T state, string etag = null) + { + var data = TypeConverters.ToJsonByteString(state, new JsonSerializerOptions(JsonSerializerDefaults.Web)); + var response = new Autogenerated.GetStateResponse { - await using var client = TestClient.CreateForDaprClient(); - - const string queryJson = "{'query':{'filter':{ 'EQ': {'value':'test'}}}}"; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.QueryStateAsync("testStore", queryJson, new Dictionary())); - - // Validate request. - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Query.ShouldBe(queryJson); - envelope.Metadata.ShouldBeEmpty(); - - // Validate response, we expect to only get the first object as the 2nd will present an error. - var testData1 = new Widget() { Color = "Green", Size = "Small" }; - var testData2 = new Widget() { Color = "Green", Size = "Medium" }; - var testData3 = new Widget() { Color = "Green", Size = "Large" }; - var wireResponse = new Autogenerated.QueryStateResponse(); - - wireResponse.Results.Add(MakeQueryStateItem("test1", testData1)); - wireResponse.Results.Add(MakeQueryStateItem("test2", testData2, string.Empty, "An error!")); - wireResponse.Results.Add(MakeQueryStateItem("test3", testData3)); - - var ex = await Assert.ThrowsAsync>(() => request.CompleteWithMessageAsync(wireResponse)); - ex.Message.ShouldBe("Encountered an error while processing state query results."); - var response = ex.Response; - response.Results.Count.ShouldBe(2); - response.Results[0].Key.ShouldBe("test1"); - response.Results[0].Data.ShouldBe(testData1); - response.Results[0].ETag.ShouldBeNullOrEmpty(); - response.Results[0].Error.ShouldBeNullOrEmpty(); - response.Results[1].Key.ShouldBe("test3"); - response.Results[1].Data.ShouldBe(testData3); - response.Results[1].ETag.ShouldBeNullOrEmpty(); - response.Results[1].Error.ShouldBeNullOrEmpty(); - - var failedKeys = ex.FailedKeys; - failedKeys.Count.ShouldBe(1); - failedKeys[0].ShouldBe("test2"); - } + Data = data + }; - private Autogenerated.GetStateResponse MakeGetStateResponse(T state, string etag = null) + if (etag != null) { - var data = TypeConverters.ToJsonByteString(state, new JsonSerializerOptions(JsonSerializerDefaults.Web)); - var response = new Autogenerated.GetStateResponse - { - Data = data - }; - - if (etag != null) - { - response.Etag = etag; - } - - return response; + response.Etag = etag; } - private Autogenerated.GetBulkStateResponse MakeGetBulkStateResponse(string key, T state) + return response; + } + + private Autogenerated.GetBulkStateResponse MakeGetBulkStateResponse(string key, T state) + { + var data = TypeConverters.ToJsonByteString(state, new JsonSerializerOptions(JsonSerializerDefaults.Web)); + var response = new Autogenerated.GetBulkStateResponse { - var data = TypeConverters.ToJsonByteString(state, new JsonSerializerOptions(JsonSerializerDefaults.Web)); - var response = new Autogenerated.GetBulkStateResponse + Items = { - Items = + new Autogenerated.BulkStateItem() { - new Autogenerated.BulkStateItem() - { - Key = key, - Data = data, - } + Key = key, + Data = data, } - }; + } + }; - return response; - } + return response; + } - private Autogenerated.QueryStateItem MakeQueryStateItem(string key, T data, string etag = default, string error = default) - { - var wireItem = new Autogenerated.QueryStateItem - { - Key = key, Data = ByteString.CopyFromUtf8(JsonSerializer.Serialize(data)), Etag = etag ?? string.Empty, - Error = error ?? string.Empty - }; - return wireItem; - } - [Theory] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] - public async Task SaveByteStateAsync_ValidateOptions( - ConsistencyMode consistencyMode, - ConcurrencyMode concurrencyMode, - StateConsistency expectedConsistency, - StateConcurrency expectedConcurrency) + private Autogenerated.QueryStateItem MakeQueryStateItem(string key, T data, string etag = default, string error = default) + { + var wireItem = new Autogenerated.QueryStateItem { - await using var client = TestClient.CreateForDaprClient(); - - const string data = "Test binary data"; - var stateBytes = Encoding.UTF8.GetBytes(data); - var stateOptions = new StateOptions - { - Concurrency = concurrencyMode, - Consistency = consistencyMode - }; + Key = key, Data = ByteString.CopyFromUtf8(JsonSerializer.Serialize(data)), Etag = etag ?? string.Empty, + Error = error ?? string.Empty + }; + return wireItem; + } + [Theory] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] + public async Task SaveByteStateAsync_ValidateOptions( + ConsistencyMode consistencyMode, + ConcurrencyMode concurrencyMode, + StateConsistency expectedConsistency, + StateConcurrency expectedConcurrency) + { + await using var client = TestClient.CreateForDaprClient(); - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; + const string data = "Test binary data"; + var stateBytes = Encoding.UTF8.GetBytes(data); + var stateOptions = new StateOptions + { + Concurrency = concurrencyMode, + Consistency = consistencyMode + }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveByteStateAsync("testStore", "test", stateBytes.AsMemory(), stateOptions, metadata); - }); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Key.ShouldBe("test"); - state.Metadata.Count.ShouldBe(2); - state.Metadata.Keys.Contains("key1").ShouldBeTrue(); - state.Metadata.Keys.Contains("key2").ShouldBeTrue(); - state.Metadata["key1"].ShouldBe("value1"); - state.Metadata["key2"].ShouldBe("value2"); - state.Options.Concurrency.ShouldBe(expectedConcurrency); - state.Options.Consistency.ShouldBe(expectedConsistency); - - var stateBinaryData = state.Value.ToStringUtf8(); - stateBinaryData.ShouldBe(data); - } + var metadata = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; - [Fact] - public async Task SaveByteStateAsync_CanSaveState() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.SaveByteStateAsync("testStore", "test", stateBytes.AsMemory(), stateOptions, metadata); + }); - const string data = "Test binary data"; - var stateBytes = Encoding.UTF8.GetBytes(data); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveByteStateAsync("testStore", "test", stateBytes.AsMemory()); - }); + request.Dismiss(); - request.Dismiss(); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Key.ShouldBe("test"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Key.ShouldBe("test"); + var stateBinaryData = state.Value.ToStringUtf8(); + stateBinaryData.ShouldBe(data); + } - var stateBinaryData = state.Value.ToStringUtf8(); - stateBinaryData.ShouldBe(data); - } + [Fact] + public async Task SaveByteStateAsync_CanSaveState() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task SaveByteStateAsync_CanClearState() + const string data = "Test binary data"; + var stateBytes = Encoding.UTF8.GetBytes(data); + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.SaveByteStateAsync("testStore", "test", stateBytes.AsMemory()); + }); - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - await daprClient.SaveByteStateAsync("testStore", "test", null); - }); + request.Dismiss(); - request.Dismiss(); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Key.ShouldBe("test"); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); + var stateBinaryData = state.Value.ToStringUtf8(); + stateBinaryData.ShouldBe(data); + } - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Key.ShouldBe("test"); - state.Value.ShouldBe(ByteString.Empty); - } + [Fact] + public async Task SaveByteStateAsync_CanClearState() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task SaveByteStateAsync_WithCancelledToken() + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); + await daprClient.SaveByteStateAsync("testStore", "test", null); + }); - var cts = new CancellationTokenSource(); - cts.Cancel(); + request.Dismiss(); - await Assert.ThrowsAsync(async () => - { - await client.InnerClient.SaveByteStateAsync("testStore", "test", null, cancellationToken: cts.Token); - }); - } + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); - [Theory] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] - [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] - public async Task TrySaveByteStateAsync_ValidateOptions( - ConsistencyMode consistencyMode, - ConcurrencyMode concurrencyMode, - StateConsistency expectedConsistency, - StateConcurrency expectedConcurrency) - { - await using var client = TestClient.CreateForDaprClient(); - const string data = "Test binary data"; - var stateBytes = Encoding.UTF8.GetBytes(data); - var stateOptions = new StateOptions - { - Concurrency = concurrencyMode, - Consistency = consistencyMode - }; + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Key.ShouldBe("test"); + state.Value.ShouldBe(ByteString.Empty); + } - var metadata = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.TrySaveByteStateAsync("testStore", "test", stateBytes.AsMemory(), "Test_Etag", stateOptions, metadata)); - - request.Dismiss(); - - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.States.Count.ShouldBe(1); - var state = envelope.States[0]; - state.Etag.Value.ShouldBe("Test_Etag"); - state.Metadata.Count.ShouldBe(2); - state.Metadata.Keys.Contains("key1").ShouldBeTrue(); - state.Metadata.Keys.Contains("key2").ShouldBeTrue(); - state.Metadata["key1"].ShouldBe("value1"); - state.Metadata["key2"].ShouldBe("value2"); - state.Options.Concurrency.ShouldBe(expectedConcurrency); - state.Options.Consistency.ShouldBe(expectedConsistency); - - var stateBinaryData = state.Value.ToStringUtf8(); - stateBinaryData.ShouldBe(data); - } + [Fact] + public async Task SaveByteStateAsync_WithCancelledToken() + { + await using var client = TestClient.CreateForDaprClient(); + + var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task TrySaveByteStateAsync_ValidateNonETagErrorThrowsException() + await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.InnerClient.SaveByteStateAsync("testStore", "test", null, cancellationToken: cts.Token); + }); + } - var response = client.CallStateApi() - .Build(); - const string data = "Test binary data"; - var stateBytes = Encoding.UTF8.GetBytes(data); - var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); + [Theory] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Eventual, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyEventual, StateConcurrency.ConcurrencyLastWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.FirstWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyFirstWrite)] + [InlineData(ConsistencyMode.Strong, ConcurrencyMode.LastWrite, StateConsistency.ConsistencyStrong, StateConcurrency.ConcurrencyLastWrite)] + public async Task TrySaveByteStateAsync_ValidateOptions( + ConsistencyMode consistencyMode, + ConcurrencyMode concurrencyMode, + StateConsistency expectedConsistency, + StateConcurrency expectedConcurrency) + { + await using var client = TestClient.CreateForDaprClient(); + const string data = "Test binary data"; + var stateBytes = Encoding.UTF8.GetBytes(data); + var stateOptions = new StateOptions + { + Concurrency = concurrencyMode, + Consistency = consistencyMode + }; + + var metadata = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.TrySaveByteStateAsync("testStore", "test", stateBytes.AsMemory(), "Test_Etag", stateOptions, metadata)); + + request.Dismiss(); + + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + var state = envelope.States[0]; + state.Etag.Value.ShouldBe("Test_Etag"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); + + var stateBinaryData = state.Value.ToStringUtf8(); + stateBinaryData.ShouldBe(data); + } - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); + [Fact] + public async Task TrySaveByteStateAsync_ValidateNonETagErrorThrowsException() + { + var client = new MockClient(); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), "someETag"); - }); - Assert.Same(rpcException, ex.InnerException); - } + var response = client.CallStateApi() + .Build(); + const string data = "Test binary data"; + var stateBytes = Encoding.UTF8.GetBytes(data); + var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); - [Fact] - public async Task TrySaveByteStateAsync_ValidateETagRelatedExceptionReturnsFalse() + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); + + var ex = await Assert.ThrowsAsync(async () => { - var client = new MockClient(); + await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), "someETag"); + }); + Assert.Same(rpcException, ex.InnerException); + } - var response = client.CallStateApi() + [Fact] + public async Task TrySaveByteStateAsync_ValidateETagRelatedExceptionReturnsFalse() + { + var client = new MockClient(); + + var response = client.CallStateApi() .Build(); - const string data = "Test binary data"; - var stateBytes = Encoding.UTF8.GetBytes(data); - var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed saving state in state store testStore")); - // Setup the mock client to throw an Rpc Exception with the expected details info - client.Mock - .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) - .Throws(rpcException); - - var operationResult = await client.DaprClient.TrySaveByteStateAsync("testStore", "test", stateBytes.AsMemory(), "invalidETag"); - Assert.False(operationResult); - } + const string data = "Test binary data"; + var stateBytes = Encoding.UTF8.GetBytes(data); + var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed saving state in state store testStore")); + // Setup the mock client to throw an Rpc Exception with the expected details info + client.Mock + .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) + .Throws(rpcException); + + var operationResult = await client.DaprClient.TrySaveByteStateAsync("testStore", "test", stateBytes.AsMemory(), "invalidETag"); + Assert.False(operationResult); + } - [Fact] - public async Task TrySaveByteStateAsync_NullEtagThrowsArgumentException() - { - var client = new MockClient(); - const string data = "Test binary data"; - var stateBytes = Encoding.UTF8.GetBytes(data); - var response = client.CallStateApi() + [Fact] + public async Task TrySaveByteStateAsync_NullEtagThrowsArgumentException() + { + var client = new MockClient(); + const string data = "Test binary data"; + var stateBytes = Encoding.UTF8.GetBytes(data); + var response = client.CallStateApi() .Build(); - await Should.ThrowAsync(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null)); - } + await Should.ThrowAsync(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null)); + } - [Fact] - public async Task TrySaveByteStateAsync_EmptyEtagDoesNotThrow() - { - var client = new MockClient(); - const string data = "Test binary data"; - var stateBytes = Encoding.UTF8.GetBytes(data); - var response = client.CallStateApi() + [Fact] + public async Task TrySaveByteStateAsync_EmptyEtagDoesNotThrow() + { + var client = new MockClient(); + const string data = "Test binary data"; + var stateBytes = Encoding.UTF8.GetBytes(data); + var response = client.CallStateApi() .Build(); - // Setup the mock client to return success - client.Mock - .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) - .Returns(response); + // Setup the mock client to return success + client.Mock + .Setup(m => m.SaveStateAsync(It.IsAny(), It.IsAny())) + .Returns(response); - var result = await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), ""); - Assert.True(result); - } - [Fact] - public async Task GetByteStateAsync_CanReadEmptyState_ReturnsDefault() - { - await using var client = TestClient.CreateForDaprClient(); + var result = await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), ""); + Assert.True(result); + } + [Fact] + public async Task GetByteStateAsync_CanReadEmptyState_ReturnsDefault() + { + await using var client = TestClient.CreateForDaprClient(); - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAsync("testStore", "test", ConsistencyMode.Eventual)); + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAsync("testStore", "test", ConsistencyMode.Eventual)); - // Create Response & Respond to request - var envelope = MakeGetByteStateResponse(null); - var state = await request.CompleteWithMessageAsync(envelope); + // Create Response & Respond to request + var envelope = MakeGetByteStateResponse(null); + var state = await request.CompleteWithMessageAsync(envelope); - // Get response and validate - state.ToArray().ShouldBeEmpty(); - } + // Get response and validate + state.ToArray().ShouldBeEmpty(); + } - [Theory] - [InlineData(ConsistencyMode.Eventual, StateConsistency.ConsistencyEventual)] - [InlineData(ConsistencyMode.Strong, StateConsistency.ConsistencyStrong)] - public async Task GetByteStateAsync_ValidateRequest(ConsistencyMode consistencyMode, StateConsistency expectedConsistencyMode) - { - await using var client = TestClient.CreateForDaprClient(); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAsync("testStore", "test", consistencyMode)); - - // Get Request & Validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); - envelope.Consistency.ShouldBe(expectedConsistencyMode); - var binaryData = Encoding.ASCII.GetBytes("test data"); - // Create Response & Respond - var state = await request.CompleteWithMessageAsync(MakeGetByteStateResponse(binaryData.AsMemory())); - var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); - // Get response and validate - stateStr.ShouldBeEquivalentTo(binaryData); - } + [Theory] + [InlineData(ConsistencyMode.Eventual, StateConsistency.ConsistencyEventual)] + [InlineData(ConsistencyMode.Strong, StateConsistency.ConsistencyStrong)] + public async Task GetByteStateAsync_ValidateRequest(ConsistencyMode consistencyMode, StateConsistency expectedConsistencyMode) + { + await using var client = TestClient.CreateForDaprClient(); + + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAsync("testStore", "test", consistencyMode)); + + // Get Request & Validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Consistency.ShouldBe(expectedConsistencyMode); + var binaryData = Encoding.ASCII.GetBytes("test data"); + // Create Response & Respond + var state = await request.CompleteWithMessageAsync(MakeGetByteStateResponse(binaryData.AsMemory())); + var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); + // Get response and validate + stateStr.ShouldBeEquivalentTo(binaryData); + } - [Fact] - public async Task GetByteStateAndEtagAsync_ValidateRequest() - { - await using var client = TestClient.CreateForDaprClient(); + [Fact] + public async Task GetByteStateAndEtagAsync_ValidateRequest() + { + await using var client = TestClient.CreateForDaprClient(); + + var metadata = new Dictionary + { + { "partitionKey", "mypartition" } + }; + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test", metadata: metadata)); + // Get Request & Validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Metadata.ShouldBe(metadata); + var binaryData = Encoding.ASCII.GetBytes("test data"); + // Create Response & Respond + var (state, etag) = await request.CompleteWithMessageAsync((MakeGetByteStateResponse(binaryData.AsMemory()))); + var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); + // Get response and validate + stateStr.ShouldBe(binaryData); + } - var metadata = new Dictionary - { - { "partitionKey", "mypartition" } - }; - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test", metadata: metadata)); - // Get Request & Validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("testStore"); - envelope.Key.ShouldBe("test"); - envelope.Metadata.ShouldBe(metadata); - var binaryData = Encoding.ASCII.GetBytes("test data"); - // Create Response & Respond - var (state, etag) = await request.CompleteWithMessageAsync((MakeGetByteStateResponse(binaryData.AsMemory()))); - var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); - // Get response and validate - stateStr.ShouldBe(binaryData); - } + [Fact] + public async Task GetByteStateAsync_WrapsRpcException() + { + await using var client = TestClient.CreateForDaprClient(); + + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAsync("testStore", "test")); - [Fact] - public async Task GetByteStateAsync_WrapsRpcException() + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAsync("testStore", "test")); + [Fact] + public async Task GetByteStateAndEtagAsync_CanReadState() + { + await using var client = TestClient.CreateForDaprClient(); + + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test")); + + // Create Response & Respond + var binaryData = Encoding.ASCII.GetBytes("test data"); + var envelope = MakeGetByteStateResponse(binaryData.AsMemory(), "Test_Etag"); + var (state, etag) = await request.CompleteWithMessageAsync(envelope); + var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); + // Get response and validate + stateStr.ShouldBeEquivalentTo(binaryData); + etag.ShouldBe("Test_Etag"); + } - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); - } + [Fact] + public async Task GetByteStateAndETagAsync_WrapsRpcException() + { + await using var client = TestClient.CreateForDaprClient(); - [Fact] - public async Task GetByteStateAndEtagAsync_CanReadState() - { - await using var client = TestClient.CreateForDaprClient(); - - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test")); - - // Create Response & Respond - var binaryData = Encoding.ASCII.GetBytes("test data"); - var envelope = MakeGetByteStateResponse(binaryData.AsMemory(), "Test_Etag"); - var (state, etag) = await request.CompleteWithMessageAsync(envelope); - var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); - // Get response and validate - stateStr.ShouldBeEquivalentTo(binaryData); - etag.ShouldBe("Test_Etag"); - } + var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test")); - [Fact] - public async Task GetByteStateAndETagAsync_WrapsRpcException() + // Create Response & Respond + var ex = await Assert.ThrowsAsync(async () => { - await using var client = TestClient.CreateForDaprClient(); + await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); + }); + Assert.IsType(ex.InnerException); + } + + private Autogenerated.GetStateResponse MakeGetByteStateResponse(ReadOnlyMemory state, string etag = null) + { - var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test")); + var response = new Autogenerated.GetStateResponse(); - // Create Response & Respond - var ex = await Assert.ThrowsAsync(async () => - { - await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.NotAcceptable)); - }); - Assert.IsType(ex.InnerException); + // convert to byte string if state is not null + if (!state.Span.IsEmpty) + { + response.Data = ByteString.CopyFrom(state.Span); } - private Autogenerated.GetStateResponse MakeGetByteStateResponse(ReadOnlyMemory state, string etag = null) + if (etag != null) { + response.Etag = etag; + } - var response = new Autogenerated.GetStateResponse(); - - // convert to byte string if state is not null - if (!state.Span.IsEmpty) - { - response.Data = ByteString.CopyFrom(state.Span); - } + return response; + } + private class Widget + { + public string Size { get; set; } - if (etag != null) - { - response.Etag = etag; - } + public string Color { get; set; } - return response; - } - private class Widget + public override bool Equals(object obj) { - public string Size { get; set; } - - public string Color { get; set; } - - public override bool Equals(object obj) - { - return obj is Widget widget && - Size == widget.Size && - Color == widget.Color; - } + return obj is Widget widget && + Size == widget.Size && + Color == widget.Color; + } - public override int GetHashCode() - { - return HashCode.Combine(Size, Color); - } + public override int GetHashCode() + { + return HashCode.Combine(Size, Color); } } } - diff --git a/test/Dapr.Client.Test/TryLockResponseTest.cs b/test/Dapr.Client.Test/TryLockResponseTest.cs index f9b6f5247..fd7489f53 100644 --- a/test/Dapr.Client.Test/TryLockResponseTest.cs +++ b/test/Dapr.Client.Test/TryLockResponseTest.cs @@ -17,55 +17,54 @@ using Shouldly; using System; -namespace Dapr.Client.Test +namespace Dapr.Client.Test; + +[System.Obsolete] +public class TryLockResponseTest { - [System.Obsolete] - public class TryLockResponseTest + [Fact] + public async Task TryLockAsync_WithAllValues_ValidateRequest() { - [Fact] - public async Task TryLockAsync_WithAllValues_ValidateRequest() + await using var client = TestClient.CreateForDaprClient(); + string storeName = "redis"; + string resourceId = "resourceId"; + string lockOwner = "owner1"; + Int32 expiryInSeconds = 1000; + var request = await client.CaptureGrpcRequestAsync(async daprClient => { - await using var client = TestClient.CreateForDaprClient(); - string storeName = "redis"; - string resourceId = "resourceId"; - string lockOwner = "owner1"; - Int32 expiryInSeconds = 1000; - var request = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.Lock(storeName, resourceId, lockOwner, expiryInSeconds); - }); + return await daprClient.Lock(storeName, resourceId, lockOwner, expiryInSeconds); + }); - // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.ShouldBe("redis"); - envelope.ResourceId.ShouldBe("resourceId"); - envelope.LockOwner.ShouldBe("owner1"); - envelope.ExpiryInSeconds.ShouldBe(1000); + // Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.ShouldBe("redis"); + envelope.ResourceId.ShouldBe("resourceId"); + envelope.LockOwner.ShouldBe("owner1"); + envelope.ExpiryInSeconds.ShouldBe(1000); - // Get response and validate - var invokeResponse = new Autogenerated.TryLockResponse{ - Success = true - }; + // Get response and validate + var invokeResponse = new Autogenerated.TryLockResponse{ + Success = true + }; - await request.CompleteWithMessageAsync(invokeResponse); + await request.CompleteWithMessageAsync(invokeResponse); - //testing unlocking + //testing unlocking - var unlockRequest = await client.CaptureGrpcRequestAsync(async daprClient => - { - return await daprClient.Unlock(storeName, resourceId, lockOwner); - }); - var unlockEnvelope = await unlockRequest.GetRequestEnvelopeAsync(); - unlockEnvelope.StoreName.ShouldBe("redis"); - unlockEnvelope.ResourceId.ShouldBe("resourceId"); - unlockEnvelope.LockOwner.ShouldBe("owner1"); + var unlockRequest = await client.CaptureGrpcRequestAsync(async daprClient => + { + return await daprClient.Unlock(storeName, resourceId, lockOwner); + }); + var unlockEnvelope = await unlockRequest.GetRequestEnvelopeAsync(); + unlockEnvelope.StoreName.ShouldBe("redis"); + unlockEnvelope.ResourceId.ShouldBe("resourceId"); + unlockEnvelope.LockOwner.ShouldBe("owner1"); - var invokeUnlockResponse = new Autogenerated.UnlockResponse{ - Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist - }; + var invokeUnlockResponse = new Autogenerated.UnlockResponse{ + Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist + }; - var domainUnlockResponse = await unlockRequest.CompleteWithMessageAsync(invokeUnlockResponse); - domainUnlockResponse.status.ShouldBe(LockStatus.LockDoesNotExist); - } + var domainUnlockResponse = await unlockRequest.CompleteWithMessageAsync(invokeUnlockResponse); + domainUnlockResponse.status.ShouldBe(LockStatus.LockDoesNotExist); } -} +} \ No newline at end of file diff --git a/test/Dapr.Client.Test/TypeConvertersTest.cs b/test/Dapr.Client.Test/TypeConvertersTest.cs index ff73de4cb..a8b19c1f2 100644 --- a/test/Dapr.Client.Test/TypeConvertersTest.cs +++ b/test/Dapr.Client.Test/TypeConvertersTest.cs @@ -11,35 +11,34 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client.Test -{ - using System.Text.Json; - using Shouldly; - using Xunit; +namespace Dapr.Client.Test; + +using System.Text.Json; +using Shouldly; +using Xunit; - public class TypeConvertersTest +public class TypeConvertersTest +{ + [Fact] + public void AnyConversion_JSON_Serialization_Deserialization() { - [Fact] - public void AnyConversion_JSON_Serialization_Deserialization() + var response = new Response() { - var response = new Response() - { - Name = "test" - }; + Name = "test" + }; - var options = new JsonSerializerOptions(JsonSerializerDefaults.Web); + var options = new JsonSerializerOptions(JsonSerializerDefaults.Web); - var any = TypeConverters.ToJsonAny(response, options); - var type = TypeConverters.FromJsonAny(any, options); + var any = TypeConverters.ToJsonAny(response, options); + var type = TypeConverters.FromJsonAny(any, options); - type.ShouldBeEquivalentTo(response); - any.TypeUrl.ShouldBe(string.Empty); - type.Name.ShouldBe("test"); - } + type.ShouldBeEquivalentTo(response); + any.TypeUrl.ShouldBe(string.Empty); + type.Name.ShouldBe("test"); + } - private class Response - { - public string Name { get; set; } - } + private class Response + { + public string Name { get; set; } } -} +} \ No newline at end of file diff --git a/test/Dapr.Common.Test/DaprExtendedErrorInfoTest.cs b/test/Dapr.Common.Test/DaprExtendedErrorInfoTest.cs index b215bb600..f2d3a2748 100644 --- a/test/Dapr.Common.Test/DaprExtendedErrorInfoTest.cs +++ b/test/Dapr.Common.Test/DaprExtendedErrorInfoTest.cs @@ -6,685 +6,684 @@ using Grpc.Core; using Xunit; -namespace Dapr.Common.Test +namespace Dapr.Common.Test; + +public class DaprExtendedErrorInfoTest { - public class DaprExtendedErrorInfoTest - { - private static int statusCode = 1; - private static string statusMessage = "Status Message"; + private static int statusCode = 1; + private static string statusMessage = "Status Message"; - [Fact] - public void DaprExendedErrorInfo_ThrowsRpcDaprException_ExtendedErrorInfoReturnsTrueAndNotNull() + [Fact] + public void DaprExendedErrorInfo_ThrowsRpcDaprException_ExtendedErrorInfoReturnsTrueAndNotNull() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; - - BadRequest badRequest = new(); + Code = statusCode, + Message = statusMessage, + }; - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.BadRequest", Value = badRequest.ToByteString() }); + BadRequest badRequest = new(); - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.BadRequest", Value = badRequest.ToByteString() }); - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "BadRequest"), trailers: trailers); - - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "BadRequest"), trailers: trailers); - Assert.NotNull(result); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExendedErrorInfo_ThrowsNonRpcDaprException_ExtendedErrorInfoReturnsFalseAndIsNull() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - // Act, Assert - try - { - throw new DaprException("Non-Rpc based Dapr exception"); - } + Assert.NotNull(result); + } - catch (DaprException daprEx) - { - Assert.False(daprEx.TryGetExtendedErrorInfo(out result)); - } + [Fact] + public void DaprExendedErrorInfo_ThrowsNonRpcDaprException_ExtendedErrorInfoReturnsFalseAndIsNull() + { + // Arrange + DaprExtendedErrorInfo result = null; - Assert.Null(result); + // Act, Assert + try + { + throw new DaprException("Non-Rpc based Dapr exception"); } - [Fact] - public void DaprExendedErrorInfo_ThrowsRpcDaprException_NoTrailers_ExtendedErrorInfoIsNull() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; + Assert.False(daprEx.TryGetExtendedErrorInfo(out result)); + } - var rpcEx = new RpcException(status: new Grpc.Core.Status()); + Assert.Null(result); + } - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + [Fact] + public void DaprExendedErrorInfo_ThrowsRpcDaprException_NoTrailers_ExtendedErrorInfoIsNull() + { + // Arrange + DaprExtendedErrorInfo result = null; - catch (DaprException daprEx) - { - Assert.False(daprEx.TryGetExtendedErrorInfo(out result)); - } + var rpcEx = new RpcException(status: new Grpc.Core.Status()); - Assert.Null(result); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsBadRequestRpcException_ShouldGetSingleDaprBadRequestDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; - - BadRequest badRequest = new(); - - var violationDescription = "Violation Description"; - var violationField = "Violation Field"; - - badRequest.FieldViolations.Add(new BadRequest.Types.FieldViolation() - { - Description = violationDescription, - Field = violationField, - }); + Assert.False(daprEx.TryGetExtendedErrorInfo(out result)); + } - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.BadRequest", Value = badRequest.ToByteString() }); + Assert.Null(result); + } - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + [Fact] + public void DaprExtendedErrorInfo_ThrowsBadRequestRpcException_ShouldGetSingleDaprBadRequestDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "BadRequest"), trailers: trailers); + BadRequest badRequest = new(); - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + var violationDescription = "Violation Description"; + var violationField = "Violation Field"; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + badRequest.FieldViolations.Add(new BadRequest.Types.FieldViolation() + { + Description = violationDescription, + Field = violationField, + }); - Assert.NotNull(result); - var detail = Assert.Single(result.Details); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.BadRequest", Value = badRequest.ToByteString() }); - Assert.Equal(DaprExtendedErrorType.BadRequest, detail.ErrorType); - var badRequestDetail = Assert.IsType(detail); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - var violation = Assert.Single(badRequestDetail.FieldViolations); + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "BadRequest"), trailers: trailers); - Assert.Equal(violation.Description, violationDescription); - Assert.Equal(violation.Field, violationField); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsLocalizedMessageRpcException_ShouldGetSingleDaprLocalizedMessageDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } + + Assert.NotNull(result); + var detail = Assert.Single(result.Details); - var localizedMessage = "Localized Message"; - var locale = "locale"; + Assert.Equal(DaprExtendedErrorType.BadRequest, detail.ErrorType); + var badRequestDetail = Assert.IsType(detail); - LocalizedMessage badRequest = new() - { - Message = localizedMessage, - Locale = locale, - }; + var violation = Assert.Single(badRequestDetail.FieldViolations); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.LocalizedMessage", Value = badRequest.ToByteString() }); + Assert.Equal(violation.Description, violationDescription); + Assert.Equal(violation.Field, violationField); + } - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + [Fact] + public void DaprExtendedErrorInfo_ThrowsLocalizedMessageRpcException_ShouldGetSingleDaprLocalizedMessageDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "BadRequest"), trailers: trailers); + var localizedMessage = "Localized Message"; + var locale = "locale"; - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + LocalizedMessage badRequest = new() + { + Message = localizedMessage, + Locale = locale, + }; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.LocalizedMessage", Value = badRequest.ToByteString() }); - Assert.NotNull(result); - var detail = Assert.Single(result.Details); - Assert.Equal(DaprExtendedErrorType.LocalizedMessage, detail.ErrorType); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - var localizedMessageDetail = Assert.IsType(detail); + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "BadRequest"), trailers: trailers); - Assert.Equal(localizedMessage, localizedMessageDetail.Message); - Assert.Equal(locale, localizedMessageDetail.Locale); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowRetryInfoRpcException_ShouldGetSingleDaprRetryInfoDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - RetryInfo retryInfo = new(); + Assert.NotNull(result); + var detail = Assert.Single(result.Details); + Assert.Equal(DaprExtendedErrorType.LocalizedMessage, detail.ErrorType); - retryInfo.RetryDelay = new Duration() - { - Seconds = 1, - Nanos = 0, - }; + var localizedMessageDetail = Assert.IsType(detail); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.RetryInfo", Value = retryInfo.ToByteString() }); + Assert.Equal(localizedMessage, localizedMessageDetail.Message); + Assert.Equal(locale, localizedMessageDetail.Locale); + } - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + [Fact] + public void DaprExtendedErrorInfo_ThrowRetryInfoRpcException_ShouldGetSingleDaprRetryInfoDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.FailedPrecondition, "RetryInfo"), trailers: trailers); + RetryInfo retryInfo = new(); - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + retryInfo.RetryDelay = new Duration() + { + Seconds = 1, + Nanos = 0, + }; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.RetryInfo", Value = retryInfo.ToByteString() }); - Assert.NotNull(result); - var detail = Assert.Single(result.Details); - Assert.Equal(DaprExtendedErrorType.RetryInfo, detail.ErrorType); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - var retryInfoDetail = Assert.IsType(detail); + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.FailedPrecondition, "RetryInfo"), trailers: trailers); - Assert.NotNull(retryInfoDetail); - Assert.Equal(1, retryInfoDetail.Delay.Seconds); - Assert.Equal(0, retryInfoDetail.Delay.Nanos); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsDebugInfoRpcException_ShouldGetSingleDaprDebugInfoDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; - - Google.Rpc.DebugInfo debugInfo = new(); + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - debugInfo.Detail = "Debug Detail"; - debugInfo.StackEntries.Add("Stack Entry"); + Assert.NotNull(result); + var detail = Assert.Single(result.Details); + Assert.Equal(DaprExtendedErrorType.RetryInfo, detail.ErrorType); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.DebugInfo", Value = debugInfo.ToByteString() }); + var retryInfoDetail = Assert.IsType(detail); - Grpc.Core.Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + Assert.NotNull(retryInfoDetail); + Assert.Equal(1, retryInfoDetail.Delay.Seconds); + Assert.Equal(0, retryInfoDetail.Delay.Nanos); + } - var rpcEx = new RpcException(status: new Grpc.Core.Status(Grpc.Core.StatusCode.FailedPrecondition, "DebugInfo"), trailers: trailers); + [Fact] + public void DaprExtendedErrorInfo_ThrowsDebugInfoRpcException_ShouldGetSingleDaprDebugInfoDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + Google.Rpc.DebugInfo debugInfo = new(); - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + debugInfo.Detail = "Debug Detail"; + debugInfo.StackEntries.Add("Stack Entry"); - Assert.NotNull(result); - var detail = Assert.Single(result.Details); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.DebugInfo", Value = debugInfo.ToByteString() }); - Assert.Equal(DaprExtendedErrorType.DebugInfo, detail.ErrorType); + Grpc.Core.Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - var daprDebugInfoDetail = Assert.IsType(detail); + var rpcEx = new RpcException(status: new Grpc.Core.Status(Grpc.Core.StatusCode.FailedPrecondition, "DebugInfo"), trailers: trailers); - Assert.Equal("Debug Detail", daprDebugInfoDetail.Detail); - var entry = Assert.Single(daprDebugInfoDetail.StackEntries); - Assert.Equal("Stack Entry", entry); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsPreconditionFailureRpcException_ShouldGetSingleDaprPreconditionFailureDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - Google.Rpc.PreconditionFailure failure = new(); + Assert.NotNull(result); + var detail = Assert.Single(result.Details); - var violationDesc = "Violation Description"; - var violationSubject = "Violation Subject"; - var violationType = "Violation Type"; + Assert.Equal(DaprExtendedErrorType.DebugInfo, detail.ErrorType); - failure.Violations.Add(new Google.Rpc.PreconditionFailure.Types.Violation() - { - Description = violationDesc, - Subject = violationSubject, - Type = violationType - }); + var daprDebugInfoDetail = Assert.IsType(detail); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.PreconditionFailure", Value = failure.ToByteString() }); + Assert.Equal("Debug Detail", daprDebugInfoDetail.Detail); + var entry = Assert.Single(daprDebugInfoDetail.StackEntries); + Assert.Equal("Stack Entry", entry); + } - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + [Fact] + public void DaprExtendedErrorInfo_ThrowsPreconditionFailureRpcException_ShouldGetSingleDaprPreconditionFailureDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - var rpcEx = new RpcException(status: new Grpc.Core.Status(Grpc.Core.StatusCode.FailedPrecondition, "PrecondtionFailure"), trailers: trailers); + Google.Rpc.PreconditionFailure failure = new(); - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + var violationDesc = "Violation Description"; + var violationSubject = "Violation Subject"; + var violationType = "Violation Type"; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + failure.Violations.Add(new Google.Rpc.PreconditionFailure.Types.Violation() + { + Description = violationDesc, + Subject = violationSubject, + Type = violationType + }); - Assert.NotNull(result); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.PreconditionFailure", Value = failure.ToByteString() }); - var detail = Assert.Single(result.Details); - Assert.Equal(DaprExtendedErrorType.PreconditionFailure, detail.ErrorType); - var preconditionFailureDetail = Assert.IsType(detail); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - var violation = Assert.Single(preconditionFailureDetail.Violations); + var rpcEx = new RpcException(status: new Grpc.Core.Status(Grpc.Core.StatusCode.FailedPrecondition, "PrecondtionFailure"), trailers: trailers); - Assert.Equal(violation.Description, violationDesc); - Assert.Equal(violation.Subject, violationSubject); - Assert.Equal(violation.Type, violationType); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsHelpRpcException_ShouldGetSingleDaprHelpDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; - - Help help = new(); + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - var helpDesc = "Help Description"; + Assert.NotNull(result); - var helpUrl = "help-link.com"; + var detail = Assert.Single(result.Details); + Assert.Equal(DaprExtendedErrorType.PreconditionFailure, detail.ErrorType); + var preconditionFailureDetail = Assert.IsType(detail); - help.Links.Add(new Help.Types.Link() - { - Description = helpDesc, - Url = helpUrl, - }); + var violation = Assert.Single(preconditionFailureDetail.Violations); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.Help", Value = help.ToByteString() }); + Assert.Equal(violation.Description, violationDesc); + Assert.Equal(violation.Subject, violationSubject); + Assert.Equal(violation.Type, violationType); + } - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + [Fact] + public void DaprExtendedErrorInfo_ThrowsHelpRpcException_ShouldGetSingleDaprHelpDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "Help"), trailers: trailers); + Help help = new(); - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + var helpDesc = "Help Description"; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + var helpUrl = "help-link.com"; - Assert.NotNull(result); - var detail = Assert.Single(result.Details); + help.Links.Add(new Help.Types.Link() + { + Description = helpDesc, + Url = helpUrl, + }); - Assert.IsAssignableFrom(detail); - Assert.Equal(DaprExtendedErrorType.Help, detail.ErrorType); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.Help", Value = help.ToByteString() }); - var helpDetail = Assert.IsType(detail); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - var link = Assert.Single(helpDetail.Links); + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "Help"), trailers: trailers); - Assert.Equal(helpDesc, link.Description); - Assert.Equal(helpUrl, link.Url); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsResourceInfoRpcException_ShouldGetSingleDaprResourceInfoDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - var resourceInfoDesc = "Description"; - var resourceInfoOwner = "Owner"; - var resourceInfoName = "Name"; - var resourceInfoType = "Type"; + Assert.NotNull(result); + var detail = Assert.Single(result.Details); - ResourceInfo resourceInfo = new() - { - Description = resourceInfoDesc, - Owner = resourceInfoOwner, - ResourceName = resourceInfoName, - ResourceType = resourceInfoType, - }; + Assert.IsAssignableFrom(detail); + Assert.Equal(DaprExtendedErrorType.Help, detail.ErrorType); + var helpDetail = Assert.IsType(detail); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.ResourceInfo", Value = resourceInfo.ToByteString() }); + var link = Assert.Single(helpDetail.Links); - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + Assert.Equal(helpDesc, link.Description); + Assert.Equal(helpUrl, link.Url); + } - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "ResourceInfo"), trailers: trailers); + [Fact] + public void DaprExtendedErrorInfo_ThrowsResourceInfoRpcException_ShouldGetSingleDaprResourceInfoDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + var resourceInfoDesc = "Description"; + var resourceInfoOwner = "Owner"; + var resourceInfoName = "Name"; + var resourceInfoType = "Type"; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + ResourceInfo resourceInfo = new() + { + Description = resourceInfoDesc, + Owner = resourceInfoOwner, + ResourceName = resourceInfoName, + ResourceType = resourceInfoType, + }; - Assert.NotNull(result); - var detail = Assert.Single(result.Details); - Assert.IsAssignableFrom(detail); - Assert.Equal(DaprExtendedErrorType.ResourceInfo, detail.ErrorType); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.ResourceInfo", Value = resourceInfo.ToByteString() }); - var daprResourceInfo = Assert.IsType(detail); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - Assert.Equal(resourceInfoDesc, daprResourceInfo.Description); - Assert.Equal(resourceInfoName, daprResourceInfo.ResourceName); - Assert.Equal(resourceInfoType, daprResourceInfo.ResourceType); - Assert.Equal(resourceInfoOwner, daprResourceInfo.Owner); - } + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "ResourceInfo"), trailers: trailers); - [Fact] - public void DaprExtendedErrorInfo_ThrowsQuotaFailureRpcException_ShouldGetSingleDaprQuotaFailureDetail() + // Act, Assert + try { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; + ThrowsRpcBasedDaprException(rpcEx); + } - var quotaFailureDesc = "Description"; - var quotaFailureSubject = "Subject"; + catch (DaprException daprEx) + { + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - QuotaFailure quotaFailure = new(); + Assert.NotNull(result); - quotaFailure.Violations.Add(new QuotaFailure.Types.Violation() - { - Description = quotaFailureDesc, - Subject = quotaFailureSubject, - }); + var detail = Assert.Single(result.Details); + Assert.IsAssignableFrom(detail); + Assert.Equal(DaprExtendedErrorType.ResourceInfo, detail.ErrorType); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.QuotaFailure", Value = quotaFailure.ToByteString() }); + var daprResourceInfo = Assert.IsType(detail); - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + Assert.Equal(resourceInfoDesc, daprResourceInfo.Description); + Assert.Equal(resourceInfoName, daprResourceInfo.ResourceName); + Assert.Equal(resourceInfoType, daprResourceInfo.ResourceType); + Assert.Equal(resourceInfoOwner, daprResourceInfo.Owner); + } - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "QuotaFailure"), trailers: trailers); + [Fact] + public void DaprExtendedErrorInfo_ThrowsQuotaFailureRpcException_ShouldGetSingleDaprQuotaFailureDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + var quotaFailureDesc = "Description"; + var quotaFailureSubject = "Subject"; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + QuotaFailure quotaFailure = new(); - Assert.NotNull(result); + quotaFailure.Violations.Add(new QuotaFailure.Types.Violation() + { + Description = quotaFailureDesc, + Subject = quotaFailureSubject, + }); - var detail = Assert.Single(result.Details); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.QuotaFailure", Value = quotaFailure.ToByteString() }); - Assert.IsAssignableFrom(detail); - Assert.Equal(DaprExtendedErrorType.QuotaFailure, detail.ErrorType); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - var quotaFailureDetail = Assert.IsType(detail); + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "QuotaFailure"), trailers: trailers); - var violation = Assert.Single(quotaFailureDetail.Violations); - Assert.Equal(quotaFailureDesc, violation.Description); - Assert.Equal(quotaFailureSubject, violation.Subject); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsErrorInfoRpcException_ShouldGetSingleDaprErrorInfoDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; - - var errorInfoDomain = "Domain"; - var errorInfoReason = "Reason"; - var errorInfoMetadataKey = "Key"; - var errorInfoMetadataValue = "Value"; - var errorInfoMetadata = new Dictionary() - { - { errorInfoMetadataKey, errorInfoMetadataValue } - }; - - Google.Rpc.ErrorInfo errorInfo = new() - { - Domain = errorInfoDomain, - Reason = errorInfoReason, - }; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - errorInfo.Metadata.Add(errorInfoMetadata); + Assert.NotNull(result); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.ErrorInfo", Value = errorInfo.ToByteString() }); + var detail = Assert.Single(result.Details); - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + Assert.IsAssignableFrom(detail); + Assert.Equal(DaprExtendedErrorType.QuotaFailure, detail.ErrorType); - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "ErrorInfo"), trailers: trailers); + var quotaFailureDetail = Assert.IsType(detail); - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + var violation = Assert.Single(quotaFailureDetail.Violations); + Assert.Equal(quotaFailureDesc, violation.Description); + Assert.Equal(quotaFailureSubject, violation.Subject); + } - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + [Fact] + public void DaprExtendedErrorInfo_ThrowsErrorInfoRpcException_ShouldGetSingleDaprErrorInfoDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; + + var errorInfoDomain = "Domain"; + var errorInfoReason = "Reason"; + var errorInfoMetadataKey = "Key"; + var errorInfoMetadataValue = "Value"; + var errorInfoMetadata = new Dictionary() + { + { errorInfoMetadataKey, errorInfoMetadataValue } + }; - Assert.NotNull(result); + Google.Rpc.ErrorInfo errorInfo = new() + { + Domain = errorInfoDomain, + Reason = errorInfoReason, + }; - var detail = Assert.Single(result.Details); + errorInfo.Metadata.Add(errorInfoMetadata); - Assert.Equal(DaprExtendedErrorType.ErrorInfo, detail.ErrorType); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.ErrorInfo", Value = errorInfo.ToByteString() }); - var errorInfoDetail = Assert.IsType(detail); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - Assert.Equal(errorInfoDomain, errorInfoDetail.Domain); - Assert.Equal(errorInfoReason, errorInfoDetail.Reason); + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "ErrorInfo"), trailers: trailers); - var metadata = Assert.Single(errorInfoDetail.Metadata); - Assert.Equal(errorInfoMetadataKey, metadata.Key); - Assert.Equal(errorInfoMetadataValue, metadata.Value); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsRequestInfoRpcException_ShouldGetSingleDaprRequestInfoDetail() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } + + Assert.NotNull(result); + + var detail = Assert.Single(result.Details); - var requestInfoId = "RequestId"; - var requestInfoServingData = "Serving Data"; + Assert.Equal(DaprExtendedErrorType.ErrorInfo, detail.ErrorType); - RequestInfo requestInfo = new() - { - RequestId = requestInfoId, - ServingData = requestInfoServingData, - }; + var errorInfoDetail = Assert.IsType(detail); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.RequestInfo", Value = requestInfo.ToByteString() }); + Assert.Equal(errorInfoDomain, errorInfoDetail.Domain); + Assert.Equal(errorInfoReason, errorInfoDetail.Reason); - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + var metadata = Assert.Single(errorInfoDetail.Metadata); + Assert.Equal(errorInfoMetadataKey, metadata.Key); + Assert.Equal(errorInfoMetadataValue, metadata.Value); + } - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "RequestInfo"), trailers: trailers); + [Fact] + public void DaprExtendedErrorInfo_ThrowsRequestInfoRpcException_ShouldGetSingleDaprRequestInfoDetail() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + var requestInfoId = "RequestId"; + var requestInfoServingData = "Serving Data"; - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + RequestInfo requestInfo = new() + { + RequestId = requestInfoId, + ServingData = requestInfoServingData, + }; - Assert.NotNull(result); - var detail = Assert.Single(result.Details); + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.RequestInfo", Value = requestInfo.ToByteString() }); - Assert.Equal(DaprExtendedErrorType.RequestInfo, detail.ErrorType); - var requestInfoDetail = Assert.IsType(detail); + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; + + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "RequestInfo"), trailers: trailers); - Assert.Equal(requestInfoId, requestInfoDetail.RequestId); - Assert.Equal(requestInfoServingData, requestInfoDetail.ServingData); + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); } - [Fact] - public void DaprExtendedErrorInfo_ThrowsRequestInfoRpcException_ShouldGetMultipleDetails() + catch (DaprException daprEx) { - // Arrange - DaprExtendedErrorInfo result = null; - var metadataEntry = new Google.Rpc.Status() - { - Code = statusCode, - Message = statusMessage, - }; + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); + } - List expectedDetailTypes = new() - { - typeof(DaprResourceInfoDetail), - typeof(DaprRequestInfoDetail), - }; + Assert.NotNull(result); + var detail = Assert.Single(result.Details); - RequestInfo requestInfo = new(); + Assert.Equal(DaprExtendedErrorType.RequestInfo, detail.ErrorType); + var requestInfoDetail = Assert.IsType(detail); - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.RequestInfo", Value = requestInfo.ToByteString() }); + Assert.Equal(requestInfoId, requestInfoDetail.RequestId); + Assert.Equal(requestInfoServingData, requestInfoDetail.ServingData); + } - ResourceInfo resourceInfo = new(); + [Fact] + public void DaprExtendedErrorInfo_ThrowsRequestInfoRpcException_ShouldGetMultipleDetails() + { + // Arrange + DaprExtendedErrorInfo result = null; + var metadataEntry = new Google.Rpc.Status() + { + Code = statusCode, + Message = statusMessage, + }; - metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.ResourceInfo", Value = resourceInfo.ToByteString() }); + List expectedDetailTypes = new() + { + typeof(DaprResourceInfoDetail), + typeof(DaprRequestInfoDetail), + }; + + RequestInfo requestInfo = new(); - Metadata trailers = new() - { - { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } - }; + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.RequestInfo", Value = requestInfo.ToByteString() }); - var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "RequestInfo"), trailers: trailers); + ResourceInfo resourceInfo = new(); - // Act, Assert - try - { - ThrowsRpcBasedDaprException(rpcEx); - } + metadataEntry.Details.Add(new Any() { TypeUrl = "type.googleapis.com/Google.rpc.ResourceInfo", Value = resourceInfo.ToByteString() }); - catch (DaprException daprEx) - { - Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); - } + Metadata trailers = new() + { + { DaprExtendedErrorConstants.GrpcDetails, metadataEntry.ToByteArray() } + }; - Assert.Collection(result.Details, - detail => Assert.Contains(detail.GetType(), expectedDetailTypes), - detail => Assert.Contains(detail.GetType(), expectedDetailTypes) - ); + var rpcEx = new RpcException(status: new Grpc.Core.Status(StatusCode.Aborted, "RequestInfo"), trailers: trailers); + + // Act, Assert + try + { + ThrowsRpcBasedDaprException(rpcEx); + } + + catch (DaprException daprEx) + { + Assert.True(daprEx.TryGetExtendedErrorInfo(out result)); } - private static void ThrowsRpcBasedDaprException(RpcException ex) => throw new DaprException("A Dapr exception", ex); + Assert.Collection(result.Details, + detail => Assert.Contains(detail.GetType(), expectedDetailTypes), + detail => Assert.Contains(detail.GetType(), expectedDetailTypes) + ); } -} + + private static void ThrowsRpcBasedDaprException(RpcException ex) => throw new DaprException("A Dapr exception", ex); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/ExceptionTesting/IExceptionActor.cs b/test/Dapr.E2E.Test.Actors/ExceptionTesting/IExceptionActor.cs index 767785292..f8a85bfae 100644 --- a/test/Dapr.E2E.Test.Actors/ExceptionTesting/IExceptionActor.cs +++ b/test/Dapr.E2E.Test.Actors/ExceptionTesting/IExceptionActor.cs @@ -13,10 +13,9 @@ using Dapr.Actors; using System.Threading.Tasks; -namespace Dapr.E2E.Test.Actors.ExceptionTesting +namespace Dapr.E2E.Test.Actors.ExceptionTesting; + +public interface IExceptionActor : IPingActor, IActor { - public interface IExceptionActor : IPingActor, IActor - { - Task ExceptionExample(); - } + Task ExceptionExample(); } \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/IPingActor.cs b/test/Dapr.E2E.Test.Actors/IPingActor.cs index 8bbe25083..176312177 100644 --- a/test/Dapr.E2E.Test.Actors/IPingActor.cs +++ b/test/Dapr.E2E.Test.Actors/IPingActor.cs @@ -14,10 +14,9 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace Dapr.E2E.Test.Actors +namespace Dapr.E2E.Test.Actors; + +public interface IPingActor : IActor { - public interface IPingActor : IActor - { - Task Ping(); - } -} + Task Ping(); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/ISerializationActor.cs b/test/Dapr.E2E.Test.Actors/ISerializationActor.cs index 46455c28d..a1c3e0471 100644 --- a/test/Dapr.E2E.Test.Actors/ISerializationActor.cs +++ b/test/Dapr.E2E.Test.Actors/ISerializationActor.cs @@ -6,19 +6,18 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace Dapr.E2E.Test.Actors +namespace Dapr.E2E.Test.Actors; + +public interface ISerializationActor : IActor, IPingActor { - public interface ISerializationActor : IActor, IPingActor - { - Task SendAsync(string name, SerializationPayload payload, CancellationToken cancellationToken = default); - Task AnotherMethod(DateTime payload); - } + Task SendAsync(string name, SerializationPayload payload, CancellationToken cancellationToken = default); + Task AnotherMethod(DateTime payload); +} - public record SerializationPayload(string Message) - { - public JsonElement Value { get; set; } +public record SerializationPayload(string Message) +{ + public JsonElement Value { get; set; } - [JsonExtensionData] - public Dictionary ExtensionData { get; set; } - } -} + [JsonExtensionData] + public Dictionary ExtensionData { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Reentrancy/CallRecord.cs b/test/Dapr.E2E.Test.Actors/Reentrancy/CallRecord.cs index 211cf2a47..946f87aa8 100644 --- a/test/Dapr.E2E.Test.Actors/Reentrancy/CallRecord.cs +++ b/test/Dapr.E2E.Test.Actors/Reentrancy/CallRecord.cs @@ -13,13 +13,12 @@ using System; -namespace Dapr.E2E.Test.Actors.Reentrancy +namespace Dapr.E2E.Test.Actors.Reentrancy; + +public class CallRecord { - public class CallRecord - { - public bool IsEnter { get; set; } - public DateTime Timestamp { get; set; } + public bool IsEnter { get; set; } + public DateTime Timestamp { get; set; } - public int CallNumber { get; set; } - } -} + public int CallNumber { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Reentrancy/IReentrantActor.cs b/test/Dapr.E2E.Test.Actors/Reentrancy/IReentrantActor.cs index 0e662b481..3b91ee768 100644 --- a/test/Dapr.E2E.Test.Actors/Reentrancy/IReentrantActor.cs +++ b/test/Dapr.E2E.Test.Actors/Reentrancy/IReentrantActor.cs @@ -14,12 +14,11 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace Dapr.E2E.Test.Actors.Reentrancy +namespace Dapr.E2E.Test.Actors.Reentrancy; + +public interface IReentrantActor : IPingActor, IActor { - public interface IReentrantActor : IPingActor, IActor - { - Task ReentrantCall(ReentrantCallOptions callOptions); + Task ReentrantCall(ReentrantCallOptions callOptions); - Task GetState(int callNumber); - } + Task GetState(int callNumber); } \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Reentrancy/ReentrantCallOptions.cs b/test/Dapr.E2E.Test.Actors/Reentrancy/ReentrantCallOptions.cs index f66e20f76..0e7d032d8 100644 --- a/test/Dapr.E2E.Test.Actors/Reentrancy/ReentrantCallOptions.cs +++ b/test/Dapr.E2E.Test.Actors/Reentrancy/ReentrantCallOptions.cs @@ -11,12 +11,11 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test.Actors.Reentrancy +namespace Dapr.E2E.Test.Actors.Reentrancy; + +public class ReentrantCallOptions { - public class ReentrantCallOptions - { - public int CallsRemaining { get; set; } + public int CallsRemaining { get; set; } - public int CallNumber { get; set; } - } + public int CallNumber { get; set; } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Reentrancy/State.cs b/test/Dapr.E2E.Test.Actors/Reentrancy/State.cs index 4a4c7a8cc..ec29cc7cc 100644 --- a/test/Dapr.E2E.Test.Actors/Reentrancy/State.cs +++ b/test/Dapr.E2E.Test.Actors/Reentrancy/State.cs @@ -13,10 +13,9 @@ using System.Collections.Generic; -namespace Dapr.E2E.Test.Actors.Reentrancy +namespace Dapr.E2E.Test.Actors.Reentrancy; + +public class State { - public class State - { - public List Records { get; set; } = new List(); - } -} + public List Records { get; set; } = new List(); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/RegressionActor/IRegression762Actor.cs b/test/Dapr.E2E.Test.Actors/RegressionActor/IRegression762Actor.cs index 471dbf23e..4a78f2fed 100644 --- a/test/Dapr.E2E.Test.Actors/RegressionActor/IRegression762Actor.cs +++ b/test/Dapr.E2E.Test.Actors/RegressionActor/IRegression762Actor.cs @@ -19,14 +19,13 @@ /// /// https://github.com/dapr/dotnet-sdk/issues/762 /// -namespace Dapr.E2E.Test.Actors.ErrorTesting +namespace Dapr.E2E.Test.Actors.ErrorTesting; + +public interface IRegression762Actor : IPingActor, IActor { - public interface IRegression762Actor : IPingActor, IActor - { - Task GetState(string id); + Task GetState(string id); - Task SaveState(StateCall call); + Task SaveState(StateCall call); - Task RemoveState(string id); - } -} + Task RemoveState(string id); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/RegressionActor/StateCall.cs b/test/Dapr.E2E.Test.Actors/RegressionActor/StateCall.cs index 9dd559474..96ce19b5b 100644 --- a/test/Dapr.E2E.Test.Actors/RegressionActor/StateCall.cs +++ b/test/Dapr.E2E.Test.Actors/RegressionActor/StateCall.cs @@ -11,12 +11,11 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test.Actors.ErrorTesting +namespace Dapr.E2E.Test.Actors.ErrorTesting; + +public class StateCall { - public class StateCall - { - public string Key { get; set; } - public string Value { get; set; } - public string Operation { get; set; } - } -} + public string Key { get; set; } + public string Value { get; set; } + public string Operation { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Reminders/IReminderActor.cs b/test/Dapr.E2E.Test.Actors/Reminders/IReminderActor.cs index c0e3f86a2..53bc4b925 100644 --- a/test/Dapr.E2E.Test.Actors/Reminders/IReminderActor.cs +++ b/test/Dapr.E2E.Test.Actors/Reminders/IReminderActor.cs @@ -15,20 +15,19 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace Dapr.E2E.Test.Actors.Reminders +namespace Dapr.E2E.Test.Actors.Reminders; + +public interface IReminderActor : IPingActor, IActor { - public interface IReminderActor : IPingActor, IActor - { - Task StartReminder(StartReminderOptions options); + Task StartReminder(StartReminderOptions options); - Task StartReminderWithTtl(TimeSpan ttl); + Task StartReminderWithTtl(TimeSpan ttl); - Task StartReminderWithRepetitions(int repetitions); + Task StartReminderWithRepetitions(int repetitions); - Task StartReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions); + Task StartReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions); - Task GetState(); + Task GetState(); - Task GetReminder(); - } -} + Task GetReminder(); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Reminders/StartReminderOptions.cs b/test/Dapr.E2E.Test.Actors/Reminders/StartReminderOptions.cs index b50d8ec2c..8e13f5d76 100644 --- a/test/Dapr.E2E.Test.Actors/Reminders/StartReminderOptions.cs +++ b/test/Dapr.E2E.Test.Actors/Reminders/StartReminderOptions.cs @@ -11,10 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test.Actors.Reminders +namespace Dapr.E2E.Test.Actors.Reminders; + +public class StartReminderOptions { - public class StartReminderOptions - { - public int Total { get; set; } - } -} + public int Total { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Reminders/State.cs b/test/Dapr.E2E.Test.Actors/Reminders/State.cs index 04d45d419..f8a979c2f 100644 --- a/test/Dapr.E2E.Test.Actors/Reminders/State.cs +++ b/test/Dapr.E2E.Test.Actors/Reminders/State.cs @@ -13,14 +13,13 @@ using System; -namespace Dapr.E2E.Test.Actors.Reminders +namespace Dapr.E2E.Test.Actors.Reminders; + +public class State { - public class State - { - public int Count { get; set; } + public int Count { get; set; } - public bool IsReminderRunning { get; set; } + public bool IsReminderRunning { get; set; } - public DateTime Timestamp { get; set; } - } -} + public DateTime Timestamp { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/State/IStateActor.cs b/test/Dapr.E2E.Test.Actors/State/IStateActor.cs index f19122102..3b63984a0 100644 --- a/test/Dapr.E2E.Test.Actors/State/IStateActor.cs +++ b/test/Dapr.E2E.Test.Actors/State/IStateActor.cs @@ -15,12 +15,11 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace Dapr.E2E.Test.Actors.State +namespace Dapr.E2E.Test.Actors.State; + +public interface IStateActor : IPingActor, IActor { - public interface IStateActor : IPingActor, IActor - { - Task GetState(string key); + Task GetState(string key); - Task SetState(string key, string value, TimeSpan? ttl); - } -} + Task SetState(string key, string value, TimeSpan? ttl); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Timers/ITimerActor.cs b/test/Dapr.E2E.Test.Actors/Timers/ITimerActor.cs index b02688fbe..5b0dbf961 100644 --- a/test/Dapr.E2E.Test.Actors/Timers/ITimerActor.cs +++ b/test/Dapr.E2E.Test.Actors/Timers/ITimerActor.cs @@ -1,28 +1,27 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 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. -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +// Copyright 2021 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 System.Threading.Tasks; using Dapr.Actors; -namespace Dapr.E2E.Test.Actors.Timers +namespace Dapr.E2E.Test.Actors.Timers; + +public interface ITimerActor : IPingActor, IActor { - public interface ITimerActor : IPingActor, IActor - { - Task StartTimer(StartTimerOptions options); + Task StartTimer(StartTimerOptions options); - Task StartTimerWithTtl(TimeSpan ttl); + Task StartTimerWithTtl(TimeSpan ttl); - Task GetState(); - } -} + Task GetState(); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Timers/StartTimerOptions.cs b/test/Dapr.E2E.Test.Actors/Timers/StartTimerOptions.cs index d226a0480..2d61f2f32 100644 --- a/test/Dapr.E2E.Test.Actors/Timers/StartTimerOptions.cs +++ b/test/Dapr.E2E.Test.Actors/Timers/StartTimerOptions.cs @@ -11,10 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test.Actors.Timers +namespace Dapr.E2E.Test.Actors.Timers; + +public class StartTimerOptions { - public class StartTimerOptions - { - public int Total { get; set; } - } -} + public int Total { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/Timers/State.cs b/test/Dapr.E2E.Test.Actors/Timers/State.cs index d47056523..e0e7fa81b 100644 --- a/test/Dapr.E2E.Test.Actors/Timers/State.cs +++ b/test/Dapr.E2E.Test.Actors/Timers/State.cs @@ -13,14 +13,13 @@ using System; -namespace Dapr.E2E.Test.Actors.Timers +namespace Dapr.E2E.Test.Actors.Timers; + +public class State { - public class State - { - public int Count { get; set; } + public int Count { get; set; } - public bool IsTimerRunning { get; set; } + public bool IsTimerRunning { get; set; } - public DateTime Timestamp { get; set; } - } -} + public DateTime Timestamp { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/DerivedResponse.cs b/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/DerivedResponse.cs index c11f547e7..e019a53fa 100644 --- a/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/DerivedResponse.cs +++ b/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/DerivedResponse.cs @@ -11,10 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting +namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting; + +public class DerivedResponse : ResponseBase { - public class DerivedResponse : ResponseBase - { - public string DerivedProperty { get; set; } - } -} + public string DerivedProperty { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/IWeaklyTypedTestingActor.cs b/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/IWeaklyTypedTestingActor.cs index 2c5d1e82d..111ae9713 100644 --- a/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/IWeaklyTypedTestingActor.cs +++ b/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/IWeaklyTypedTestingActor.cs @@ -14,12 +14,11 @@ using System.Threading.Tasks; using Dapr.Actors; -namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting +namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting; + +public interface IWeaklyTypedTestingActor : IPingActor, IActor { - public interface IWeaklyTypedTestingActor : IPingActor, IActor - { - Task GetPolymorphicResponse(); + Task GetPolymorphicResponse(); - Task GetNullResponse(); - } -} + Task GetNullResponse(); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/ResponseBase.cs b/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/ResponseBase.cs index 45736d808..bd6977909 100644 --- a/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/ResponseBase.cs +++ b/test/Dapr.E2E.Test.Actors/WeaklyTypedTesting/ResponseBase.cs @@ -13,13 +13,11 @@ using System.Text.Json.Serialization; -namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting -{ +namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting; #if NET7_0_OR_GREATER - [JsonDerivedType(typeof(DerivedResponse), typeDiscriminator: nameof(DerivedResponse))] +[JsonDerivedType(typeof(DerivedResponse), typeDiscriminator: nameof(DerivedResponse))] #endif - public class ResponseBase - { - public string BasePropeprty { get; set; } - } -} +public class ResponseBase +{ + public string BasePropeprty { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App.Grpc/MessageRepository.cs b/test/Dapr.E2E.Test.App.Grpc/MessageRepository.cs index fd70a11cc..5de6bde9f 100644 --- a/test/Dapr.E2E.Test.App.Grpc/MessageRepository.cs +++ b/test/Dapr.E2E.Test.App.Grpc/MessageRepository.cs @@ -13,36 +13,35 @@ using System.Collections.Concurrent; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public class MessageRepository { - public class MessageRepository - { - private readonly ConcurrentDictionary> messages; + private readonly ConcurrentDictionary> messages; - public MessageRepository() - { - this.messages = new ConcurrentDictionary>(); - } + public MessageRepository() + { + this.messages = new ConcurrentDictionary>(); + } - public void AddMessage(string recipient, string message) + public void AddMessage(string recipient, string message) + { + if (!this.messages.ContainsKey(recipient)) { - if (!this.messages.ContainsKey(recipient)) - { - this.messages.TryAdd(recipient, new ConcurrentQueue()); - } - this.messages[recipient].Enqueue(message); + this.messages.TryAdd(recipient, new ConcurrentQueue()); } + this.messages[recipient].Enqueue(message); + } - public string GetMessage(string recipient) + public string GetMessage(string recipient) + { + if (this.messages.TryGetValue(recipient, out var messages) && !messages.IsEmpty) { - if (this.messages.TryGetValue(recipient, out var messages) && !messages.IsEmpty) + if (messages.TryDequeue(out var message)) { - if (messages.TryDequeue(out var message)) - { - return message; - } + return message; } - return string.Empty; } + return string.Empty; } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App.Grpc/Program.cs b/test/Dapr.E2E.Test.App.Grpc/Program.cs index 9ea64dc2d..7d7543a0d 100644 --- a/test/Dapr.E2E.Test.App.Grpc/Program.cs +++ b/test/Dapr.E2E.Test.App.Grpc/Program.cs @@ -1,41 +1,40 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 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. -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +// Copyright 2021 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.Security.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Hosting; -namespace Dapr.E2E.Test.App.Grpc +namespace Dapr.E2E.Test.App.Grpc; + +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } + CreateHostBuilder(args).Build().Run(); + } - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseKestrel(options => { - options.ListenLocalhost(int.Parse(args[0]), o => o.Protocols = HttpProtocols.Http2); - options.ConfigureHttpsDefaults(httpOptions => { - httpOptions.SslProtocols = SslProtocols.Tls12; - }); + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseKestrel(options => { + options.ListenLocalhost(int.Parse(args[0]), o => o.Protocols = HttpProtocols.Http2); + options.ConfigureHttpsDefaults(httpOptions => { + httpOptions.SslProtocols = SslProtocols.Tls12; }); - webBuilder.UseStartup(); }); - } -} + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App.Grpc/Services/MessagerService.cs b/test/Dapr.E2E.Test.App.Grpc/Services/MessagerService.cs index 950668932..cfa6e0430 100644 --- a/test/Dapr.E2E.Test.App.Grpc/Services/MessagerService.cs +++ b/test/Dapr.E2E.Test.App.Grpc/Services/MessagerService.cs @@ -16,40 +16,39 @@ using Google.Protobuf.WellKnownTypes; using Grpc.Core; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public class MessagerService : Messager.MessagerBase { - public class MessagerService : Messager.MessagerBase - { - private readonly MessageRepository repository; + private readonly MessageRepository repository; - public MessagerService(MessageRepository repository) - { - this.repository = repository; - } + public MessagerService(MessageRepository repository) + { + this.repository = repository; + } - public override Task SendMessage(SendMessageRequest request, ServerCallContext context) - { - this.repository.AddMessage(request.Recipient, request.Message); - return Task.FromResult(new Empty()); - } + public override Task SendMessage(SendMessageRequest request, ServerCallContext context) + { + this.repository.AddMessage(request.Recipient, request.Message); + return Task.FromResult(new Empty()); + } - public override Task GetMessage(GetMessageRequest request, ServerCallContext context) - { - return Task.FromResult(new MessageResponse { Message = this.repository.GetMessage(request.Recipient) }); - } + public override Task GetMessage(GetMessageRequest request, ServerCallContext context) + { + return Task.FromResult(new MessageResponse { Message = this.repository.GetMessage(request.Recipient) }); + } - public override async Task StreamBroadcast(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) + public override async Task StreamBroadcast(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) + { + await foreach(var request in requestStream.ReadAllAsync()) { - await foreach(var request in requestStream.ReadAllAsync()) - { - await responseStream.WriteAsync(new MessageResponse { Message = request.Message }); - } + await responseStream.WriteAsync(new MessageResponse { Message = request.Message }); } + } - public override async Task DelayedResponse(Empty request, ServerCallContext context) - { - await Task.Delay(TimeSpan.FromSeconds(2)); - return new Empty(); - } + public override async Task DelayedResponse(Empty request, ServerCallContext context) + { + await Task.Delay(TimeSpan.FromSeconds(2)); + return new Empty(); } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App.Grpc/Startup.cs b/test/Dapr.E2E.Test.App.Grpc/Startup.cs index 651f0c76b..aac2dc705 100644 --- a/test/Dapr.E2E.Test.App.Grpc/Startup.cs +++ b/test/Dapr.E2E.Test.App.Grpc/Startup.cs @@ -16,32 +16,31 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace Dapr.E2E.Test.App.Grpc +namespace Dapr.E2E.Test.App.Grpc; + +public class Startup { - public class Startup + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - services.AddGrpc(); - services.AddSingleton(); - } + services.AddGrpc(); + services.AddSingleton(); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGrpcService(); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + }); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App.ReentrantActor/Actors/ReentrantActor.cs b/test/Dapr.E2E.Test.App.ReentrantActor/Actors/ReentrantActor.cs index 58776fe28..f59d96a5a 100644 --- a/test/Dapr.E2E.Test.App.ReentrantActor/Actors/ReentrantActor.cs +++ b/test/Dapr.E2E.Test.App.ReentrantActor/Actors/ReentrantActor.cs @@ -14,57 +14,56 @@ using System.Threading.Tasks; using Dapr.Actors.Runtime; -namespace Dapr.E2E.Test.Actors.Reentrancy +namespace Dapr.E2E.Test.Actors.Reentrancy; + +public class ReentrantActor : Actor, IReentrantActor { - public class ReentrantActor : Actor, IReentrantActor + public ReentrantActor(ActorHost host) + : base(host) { - public ReentrantActor(ActorHost host) - : base(host) - { - } + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; + } - // An actor method that exercises reentrancy by calling more methods in the same actor. - // Can be configured to different reentrant depths via the ReentrantCallOptions but will - // always make at least one additional call. - public async Task ReentrantCall(ReentrantCallOptions callOptions) - { - await UpdateState(true, callOptions.CallNumber); - var actor = this.ProxyFactory.CreateActorProxy(this.Id, "ReentrantActor"); - if (callOptions == null || callOptions.CallsRemaining <= 1) - { - await actor.Ping(); - } - else - { - await actor.ReentrantCall(new ReentrantCallOptions - { - CallsRemaining = callOptions.CallsRemaining - 1, - CallNumber = callOptions.CallNumber + 1, - }); - } - await UpdateState(false, callOptions.CallNumber); + // An actor method that exercises reentrancy by calling more methods in the same actor. + // Can be configured to different reentrant depths via the ReentrantCallOptions but will + // always make at least one additional call. + public async Task ReentrantCall(ReentrantCallOptions callOptions) + { + await UpdateState(true, callOptions.CallNumber); + var actor = this.ProxyFactory.CreateActorProxy(this.Id, "ReentrantActor"); + if (callOptions == null || callOptions.CallsRemaining <= 1) + { + await actor.Ping(); } - - public Task GetState(int callNumber) + else { - return this.StateManager.GetOrAddStateAsync($"reentrant-record{callNumber}", new State()); + await actor.ReentrantCall(new ReentrantCallOptions + { + CallsRemaining = callOptions.CallsRemaining - 1, + CallNumber = callOptions.CallNumber + 1, + }); } + await UpdateState(false, callOptions.CallNumber); + } - private async Task UpdateState(bool isEnter, int callNumber) - { - var state = await this.StateManager.GetOrAddStateAsync($"reentrant-record{callNumber}", new State()); - state.Records.Add(new CallRecord { IsEnter = isEnter, Timestamp = System.DateTime.Now, CallNumber = callNumber }); - await this.StateManager.SetStateAsync($"reentrant-record{callNumber}", state); + public Task GetState(int callNumber) + { + return this.StateManager.GetOrAddStateAsync($"reentrant-record{callNumber}", new State()); + } + + private async Task UpdateState(bool isEnter, int callNumber) + { + var state = await this.StateManager.GetOrAddStateAsync($"reentrant-record{callNumber}", new State()); + state.Records.Add(new CallRecord { IsEnter = isEnter, Timestamp = System.DateTime.Now, CallNumber = callNumber }); + await this.StateManager.SetStateAsync($"reentrant-record{callNumber}", state); - if (!isEnter) - { - await this.StateManager.SaveStateAsync(); - } + if (!isEnter) + { + await this.StateManager.SaveStateAsync(); } } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App.ReentrantActor/Program.cs b/test/Dapr.E2E.Test.App.ReentrantActor/Program.cs index 7914b6955..b6af22e68 100644 --- a/test/Dapr.E2E.Test.App.ReentrantActor/Program.cs +++ b/test/Dapr.E2E.Test.App.ReentrantActor/Program.cs @@ -14,20 +14,19 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -namespace Dapr.E2E.Test.App.ReentrantActors +namespace Dapr.E2E.Test.App.ReentrantActors; + +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + CreateHostBuilder(args).Build().Run(); } -} + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App.ReentrantActor/Startup.cs b/test/Dapr.E2E.Test.App.ReentrantActor/Startup.cs index 89d07e235..bffc65918 100644 --- a/test/Dapr.E2E.Test.App.ReentrantActor/Startup.cs +++ b/test/Dapr.E2E.Test.App.ReentrantActor/Startup.cs @@ -1,14 +1,14 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 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. +// ------------------------------------------------------------------------ +// Copyright 2021 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 Dapr.E2E.Test.Actors.Reentrancy; @@ -17,42 +17,41 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace Dapr.E2E.Test.App.ReentrantActors +namespace Dapr.E2E.Test.App.ReentrantActors; + +public class Startup { - public class Startup + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) + services.AddActors(options => { - services.AddActors(options => - { - // We force this to use a per-actor config as an easy way to validate that's working. - options.ReentrancyConfig = new() { Enabled = false }; - options.Actors.RegisterActor(typeOptions: new() - { - ReentrancyConfig = new() - { - Enabled = true, - } - }); + // We force this to use a per-actor config as an easy way to validate that's working. + options.ReentrancyConfig = new() { Enabled = false }; + options.Actors.RegisterActor(typeOptions: new() + { + ReentrancyConfig = new() + { + Enabled = true, + } }); - } + }); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseRouting(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers(); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapActorsHandlers(); + }); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Account.cs b/test/Dapr.E2E.Test.App/Account.cs index 7ba1ce11e..3e789fe0b 100644 --- a/test/Dapr.E2E.Test.App/Account.cs +++ b/test/Dapr.E2E.Test.App/Account.cs @@ -11,21 +11,20 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +/// +/// Class representing an Account for samples. +/// +public class Account { /// - /// Class representing an Account for samples. + /// Gets or sets account id. /// - public class Account - { - /// - /// Gets or sets account id. - /// - public string Id { get; set; } + public string Id { get; set; } - /// - /// Gets or sets account balance. - /// - public decimal Balance { get; set; } - } + /// + /// Gets or sets account balance. + /// + public decimal Balance { get; set; } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Actors/ExceptionActor.cs b/test/Dapr.E2E.Test.App/Actors/ExceptionActor.cs index 7dbb5bb83..d2f1ed8b3 100644 --- a/test/Dapr.E2E.Test.App/Actors/ExceptionActor.cs +++ b/test/Dapr.E2E.Test.App/Actors/ExceptionActor.cs @@ -13,23 +13,22 @@ using Dapr.Actors.Runtime; using System.Threading.Tasks; -namespace Dapr.E2E.Test.Actors.ExceptionTesting +namespace Dapr.E2E.Test.Actors.ExceptionTesting; + +public class ExceptionActor : Actor, IExceptionActor { - public class ExceptionActor : Actor, IExceptionActor + public ExceptionActor(ActorHost host) + : base(host) { - public ExceptionActor(ActorHost host) - : base(host) - { - } + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; + } - public Task ExceptionExample() - { - throw new System.Exception(); - } + public Task ExceptionExample() + { + throw new System.Exception(); } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Actors/Regression762Actor.cs b/test/Dapr.E2E.Test.App/Actors/Regression762Actor.cs index 8e7524752..602e6598c 100644 --- a/test/Dapr.E2E.Test.App/Actors/Regression762Actor.cs +++ b/test/Dapr.E2E.Test.App/Actors/Regression762Actor.cs @@ -16,50 +16,49 @@ using Dapr.Actors.Runtime; using Dapr.E2E.Test.Actors.ErrorTesting; -namespace Dapr.E2E.Test.App.ErrorTesting +namespace Dapr.E2E.Test.App.ErrorTesting; + +public class Regression762Actor : Actor, IRegression762Actor { - public class Regression762Actor : Actor, IRegression762Actor + public Regression762Actor(ActorHost host) : base(host) { - public Regression762Actor(ActorHost host) : base(host) - { - } + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; + } - public async Task GetState(string id) + public async Task GetState(string id) + { + var data = await this.StateManager.TryGetStateAsync(id); + + if (data.HasValue) { - var data = await this.StateManager.TryGetStateAsync(id); + return data.Value; + } + return string.Empty; + } - if (data.HasValue) - { - return data.Value; - } - return string.Empty; - } + public async Task RemoveState(string id) + { + await this.StateManager.TryRemoveStateAsync(id); + } - public async Task RemoveState(string id) + public async Task SaveState(StateCall call) + { + if (call.Operation == "ThrowException") { - await this.StateManager.TryRemoveStateAsync(id); + await this.StateManager.SetStateAsync(call.Key, call.Value); + throw new NotImplementedException(); } - - public async Task SaveState(StateCall call) + else if (call.Operation == "SetState") + { + await this.StateManager.SetStateAsync(call.Key, call.Value); + } + else if (call.Operation == "SaveState") { - if (call.Operation == "ThrowException") - { - await this.StateManager.SetStateAsync(call.Key, call.Value); - throw new NotImplementedException(); - } - else if (call.Operation == "SetState") - { - await this.StateManager.SetStateAsync(call.Key, call.Value); - } - else if (call.Operation == "SaveState") - { - await this.StateManager.SaveStateAsync(); - } + await this.StateManager.SaveStateAsync(); } } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Actors/ReminderActor.cs b/test/Dapr.E2E.Test.App/Actors/ReminderActor.cs index 57536377d..33ebd81fe 100644 --- a/test/Dapr.E2E.Test.App/Actors/ReminderActor.cs +++ b/test/Dapr.E2E.Test.App/Actors/ReminderActor.cs @@ -16,93 +16,92 @@ using System.Threading.Tasks; using Dapr.Actors.Runtime; -namespace Dapr.E2E.Test.Actors.Reminders +namespace Dapr.E2E.Test.Actors.Reminders; + +public class ReminderActor : Actor, IReminderActor, IRemindable { - public class ReminderActor : Actor, IReminderActor, IRemindable + public ReminderActor(ActorHost host) + : base(host) { - public ReminderActor(ActorHost host) - : base(host) - { - } + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; + } - public Task GetState() - { - return this.StateManager.GetOrAddStateAsync("reminder-state", new State()); - } + public Task GetState() + { + return this.StateManager.GetOrAddStateAsync("reminder-state", new State()); + } - // Starts a reminder that will fire N times before stopping itself - public async Task StartReminder(StartReminderOptions options) - { - var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); - await this.RegisterReminderAsync("test-reminder", bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromMilliseconds(50)); + // Starts a reminder that will fire N times before stopping itself + public async Task StartReminder(StartReminderOptions options) + { + var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); + await this.RegisterReminderAsync("test-reminder", bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromMilliseconds(50)); - await this.StateManager.SetStateAsync("reminder-state", new State(){ IsReminderRunning = true, }); - } + await this.StateManager.SetStateAsync("reminder-state", new State(){ IsReminderRunning = true, }); + } - public async Task GetReminder(){ - var reminder = await this.GetReminderAsync("test-reminder"); - var reminderString = JsonSerializer.Serialize(reminder, this.Host.JsonSerializerOptions); - return reminderString; - } + public async Task GetReminder(){ + var reminder = await this.GetReminderAsync("test-reminder"); + var reminderString = JsonSerializer.Serialize(reminder, this.Host.JsonSerializerOptions); + return reminderString; + } - public async Task StartReminderWithTtl(TimeSpan ttl) + public async Task StartReminderWithTtl(TimeSpan ttl) + { + var options = new StartReminderOptions() { - var options = new StartReminderOptions() - { - Total = 100, - }; - var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); - await this.RegisterReminderAsync("test-reminder-ttl", bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(1), ttl: ttl); - await this.StateManager.SetStateAsync("reminder-state", new State() { IsReminderRunning = true, }); - } + Total = 100, + }; + var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); + await this.RegisterReminderAsync("test-reminder-ttl", bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(1), ttl: ttl); + await this.StateManager.SetStateAsync("reminder-state", new State() { IsReminderRunning = true, }); + } - public async Task StartReminderWithRepetitions(int repetitions) + public async Task StartReminderWithRepetitions(int repetitions) + { + var options = new StartReminderOptions() { - var options = new StartReminderOptions() - { - Total = 100, - }; - var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); - await this.RegisterReminderAsync("test-reminder-repetition", bytes, dueTime: TimeSpan.Zero, - period: TimeSpan.FromSeconds(1), repetitions: repetitions); - await this.StateManager.SetStateAsync("reminder-state", new State() - { IsReminderRunning = true, }); - } + Total = 100, + }; + var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); + await this.RegisterReminderAsync("test-reminder-repetition", bytes, dueTime: TimeSpan.Zero, + period: TimeSpan.FromSeconds(1), repetitions: repetitions); + await this.StateManager.SetStateAsync("reminder-state", new State() + { IsReminderRunning = true, }); + } - public async Task StartReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions) + public async Task StartReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions) + { + var options = new StartReminderOptions() { - var options = new StartReminderOptions() - { - Total = 100, - }; - var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); - await this.RegisterReminderAsync("test-reminder-ttl-repetition", bytes, dueTime: TimeSpan.Zero, - period: TimeSpan.FromSeconds(1), repetitions: repetitions, ttl: ttl); - await this.StateManager.SetStateAsync("reminder-state", new State() - { IsReminderRunning = true, }); - } + Total = 100, + }; + var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); + await this.RegisterReminderAsync("test-reminder-ttl-repetition", bytes, dueTime: TimeSpan.Zero, + period: TimeSpan.FromSeconds(1), repetitions: repetitions, ttl: ttl); + await this.StateManager.SetStateAsync("reminder-state", new State() + { IsReminderRunning = true, }); + } - public async Task ReceiveReminderAsync(string reminderName, byte[] bytes, TimeSpan dueTime, TimeSpan period) + public async Task ReceiveReminderAsync(string reminderName, byte[] bytes, TimeSpan dueTime, TimeSpan period) + { + if (!reminderName.StartsWith("test-reminder")) { - if (!reminderName.StartsWith("test-reminder")) - { - return; - } - var options = JsonSerializer.Deserialize(bytes, this.Host.JsonSerializerOptions); - var state = await this.StateManager.GetStateAsync("reminder-state"); + return; + } + var options = JsonSerializer.Deserialize(bytes, this.Host.JsonSerializerOptions); + var state = await this.StateManager.GetStateAsync("reminder-state"); - if (++state.Count == options.Total) - { - await this.UnregisterReminderAsync("test-reminder"); - state.IsReminderRunning = false; - } - state.Timestamp = DateTime.Now; - await this.StateManager.SetStateAsync("reminder-state", state); + if (++state.Count == options.Total) + { + await this.UnregisterReminderAsync("test-reminder"); + state.IsReminderRunning = false; } + state.Timestamp = DateTime.Now; + await this.StateManager.SetStateAsync("reminder-state", state); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Actors/SerializationActor.cs b/test/Dapr.E2E.Test.App/Actors/SerializationActor.cs index 09e650fae..1614eb06a 100644 --- a/test/Dapr.E2E.Test.App/Actors/SerializationActor.cs +++ b/test/Dapr.E2E.Test.App/Actors/SerializationActor.cs @@ -4,28 +4,27 @@ using System.Threading.Tasks; using Dapr.Actors.Runtime; -namespace Dapr.E2E.Test.Actors.Serialization +namespace Dapr.E2E.Test.Actors.Serialization; + +public class SerializationActor : Actor, ISerializationActor { - public class SerializationActor : Actor, ISerializationActor + public SerializationActor(ActorHost host) + : base(host) { - public SerializationActor(ActorHost host) - : base(host) - { - } + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; + } - public Task SendAsync(string name, - SerializationPayload payload, CancellationToken cancellationToken = default) - { - return Task.FromResult(payload); - } + public Task SendAsync(string name, + SerializationPayload payload, CancellationToken cancellationToken = default) + { + return Task.FromResult(payload); + } - public Task AnotherMethod(DateTime payload){ - return Task.FromResult(payload); - } + public Task AnotherMethod(DateTime payload){ + return Task.FromResult(payload); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Actors/StateActor.cs b/test/Dapr.E2E.Test.App/Actors/StateActor.cs index 76e9745ff..22e913142 100644 --- a/test/Dapr.E2E.Test.App/Actors/StateActor.cs +++ b/test/Dapr.E2E.Test.App/Actors/StateActor.cs @@ -15,32 +15,31 @@ using System.Threading.Tasks; using Dapr.Actors.Runtime; -namespace Dapr.E2E.Test.Actors.State +namespace Dapr.E2E.Test.Actors.State; + +public class StateActor : Actor, IStateActor { - public class StateActor : Actor, IStateActor + public StateActor(ActorHost host) + : base(host) { - public StateActor(ActorHost host) - : base(host) - { - } + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; + } - public Task GetState(string key) - { - return this.StateManager.GetStateAsync(key); - } + public Task GetState(string key) + { + return this.StateManager.GetStateAsync(key); + } - public Task SetState(string key, string value, TimeSpan? ttl) + public Task SetState(string key, string value, TimeSpan? ttl) + { + if (ttl.HasValue) { - if (ttl.HasValue) - { - return this.StateManager.SetStateAsync(key, value, ttl: ttl.Value); - } - return this.StateManager.SetStateAsync(key, value); + return this.StateManager.SetStateAsync(key, value, ttl: ttl.Value); } + return this.StateManager.SetStateAsync(key, value); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Actors/TimerActor.cs b/test/Dapr.E2E.Test.App/Actors/TimerActor.cs index 14b9fef4e..76b4fe89a 100644 --- a/test/Dapr.E2E.Test.App/Actors/TimerActor.cs +++ b/test/Dapr.E2E.Test.App/Actors/TimerActor.cs @@ -16,57 +16,56 @@ using System.Threading.Tasks; using Dapr.Actors.Runtime; -namespace Dapr.E2E.Test.Actors.Timers +namespace Dapr.E2E.Test.Actors.Timers; + +public class TimerActor : Actor, ITimerActor { - public class TimerActor : Actor, ITimerActor + public TimerActor(ActorHost host) + : base(host) { - public TimerActor(ActorHost host) - : base(host) - { - } + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; + } - public Task GetState() - { - return this.StateManager.GetOrAddStateAsync("timer-state", new State()); - } + public Task GetState() + { + return this.StateManager.GetOrAddStateAsync("timer-state", new State()); + } - // Starts a timer that will fire N times before stopping itself - public async Task StartTimer(StartTimerOptions options) - { - var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); - await this.RegisterTimerAsync("test-timer", nameof(Tick), bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromMilliseconds(100)); + // Starts a timer that will fire N times before stopping itself + public async Task StartTimer(StartTimerOptions options) + { + var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); + await this.RegisterTimerAsync("test-timer", nameof(Tick), bytes, dueTime: TimeSpan.Zero, period: TimeSpan.FromMilliseconds(100)); - await this.StateManager.SetStateAsync("timer-state", new State(){ IsTimerRunning = true, }); - } + await this.StateManager.SetStateAsync("timer-state", new State(){ IsTimerRunning = true, }); + } - public async Task StartTimerWithTtl(TimeSpan ttl) + public async Task StartTimerWithTtl(TimeSpan ttl) + { + var options = new StartTimerOptions() { - var options = new StartTimerOptions() - { - Total = 100, - }; - var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); - await this.RegisterTimerAsync("test-timer-ttl", nameof(Tick), bytes, TimeSpan.Zero, TimeSpan.FromSeconds(1), ttl); - await this.StateManager.SetStateAsync("timer-state", new State() { IsTimerRunning = true, }); - } + Total = 100, + }; + var bytes = JsonSerializer.SerializeToUtf8Bytes(options, this.Host.JsonSerializerOptions); + await this.RegisterTimerAsync("test-timer-ttl", nameof(Tick), bytes, TimeSpan.Zero, TimeSpan.FromSeconds(1), ttl); + await this.StateManager.SetStateAsync("timer-state", new State() { IsTimerRunning = true, }); + } - private async Task Tick(byte[] bytes) - { - var options = JsonSerializer.Deserialize(bytes, this.Host.JsonSerializerOptions); - var state = await this.StateManager.GetStateAsync("timer-state"); + private async Task Tick(byte[] bytes) + { + var options = JsonSerializer.Deserialize(bytes, this.Host.JsonSerializerOptions); + var state = await this.StateManager.GetStateAsync("timer-state"); - if (++state.Count == options.Total) - { - await this.UnregisterTimerAsync("test-timer"); - state.IsTimerRunning = false; - } - state.Timestamp = DateTime.Now; - await this.StateManager.SetStateAsync("timer-state", state); + if (++state.Count == options.Total) + { + await this.UnregisterTimerAsync("test-timer"); + state.IsTimerRunning = false; } + state.Timestamp = DateTime.Now; + await this.StateManager.SetStateAsync("timer-state", state); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Actors/WeaklyTypedTestingActor.cs b/test/Dapr.E2E.Test.App/Actors/WeaklyTypedTestingActor.cs index 3cbe20bad..23015f994 100644 --- a/test/Dapr.E2E.Test.App/Actors/WeaklyTypedTestingActor.cs +++ b/test/Dapr.E2E.Test.App/Actors/WeaklyTypedTestingActor.cs @@ -14,34 +14,33 @@ using System.Threading.Tasks; using Dapr.Actors.Runtime; -namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting +namespace Dapr.E2E.Test.Actors.WeaklyTypedTesting; + +public class WeaklyTypedTestingActor : Actor, IWeaklyTypedTestingActor { - public class WeaklyTypedTestingActor : Actor, IWeaklyTypedTestingActor + public WeaklyTypedTestingActor(ActorHost host) + : base(host) { - public WeaklyTypedTestingActor(ActorHost host) - : base(host) - { - } + } - public Task GetNullResponse() - { - return Task.FromResult(null); - } + public Task GetNullResponse() + { + return Task.FromResult(null); + } - public Task GetPolymorphicResponse() + public Task GetPolymorphicResponse() + { + var response = new DerivedResponse { - var response = new DerivedResponse - { - BasePropeprty = "Base property value", - DerivedProperty = "Derived property value" - }; + BasePropeprty = "Base property value", + DerivedProperty = "Derived property value" + }; - return Task.FromResult(response); - } + return Task.FromResult(response); + } - public Task Ping() - { - return Task.CompletedTask; - } + public Task Ping() + { + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Controllers/TestController.cs b/test/Dapr.E2E.Test.App/Controllers/TestController.cs index 1f9274d0e..628b4b714 100644 --- a/test/Dapr.E2E.Test.App/Controllers/TestController.cs +++ b/test/Dapr.E2E.Test.App/Controllers/TestController.cs @@ -11,66 +11,65 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test -{ - using System; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Extensions.Logging; +namespace Dapr.E2E.Test; + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +/// +/// Test App invoked by the end-to-end tests +/// +[ApiController] +public class TestController : ControllerBase +{ /// - /// Test App invoked by the end-to-end tests + /// TestController Constructor with logger injection /// - [ApiController] - public class TestController : ControllerBase + /// + public TestController(ILogger logger) { - /// - /// TestController Constructor with logger injection - /// - /// - public TestController(ILogger logger) - { - this.logger = logger; - } - - private readonly ILogger logger; + this.logger = logger; + } - /// - /// Returns the account details - /// - /// Transaction to process. - /// Account - [HttpPost("accountDetails")] - public ActionResult AccountDetails(Transaction transaction) - { - var account = new Account() - { - Id = transaction.Id, - Balance = transaction.Amount + 100 - }; - return account; - } + private readonly ILogger logger; - [Authorize("Dapr")] - [HttpPost("accountDetails-requires-api-token")] - public ActionResult AccountDetailsRequiresApiToken(Transaction transaction) + /// + /// Returns the account details + /// + /// Transaction to process. + /// Account + [HttpPost("accountDetails")] + public ActionResult AccountDetails(Transaction transaction) + { + var account = new Account() { - var account = new Account() - { - Id = transaction.Id, - Balance = transaction.Amount + 100 - }; - return account; - } + Id = transaction.Id, + Balance = transaction.Amount + 100 + }; + return account; + } - [Authorize("Dapr")] - [HttpGet("DelayedResponse")] - public async Task DelayedResponse() + [Authorize("Dapr")] + [HttpPost("accountDetails-requires-api-token")] + public ActionResult AccountDetailsRequiresApiToken(Transaction transaction) + { + var account = new Account() { - await Task.Delay(TimeSpan.FromSeconds(2)); - return Ok(); - } + Id = transaction.Id, + Balance = transaction.Amount + 100 + }; + return account; + } + [Authorize("Dapr")] + [HttpGet("DelayedResponse")] + public async Task DelayedResponse() + { + await Task.Delay(TimeSpan.FromSeconds(2)); + return Ok(); } -} + +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Program.cs b/test/Dapr.E2E.Test.App/Program.cs index 5713129d5..9616f5c85 100644 --- a/test/Dapr.E2E.Test.App/Program.cs +++ b/test/Dapr.E2E.Test.App/Program.cs @@ -10,24 +10,23 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Serilog; +public class Program { - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; - using Serilog; - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - Log.Logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }).UseSerilog(); + Log.Logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); + CreateHostBuilder(args).Build().Run(); } -} + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }).UseSerilog(); +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Startup.cs b/test/Dapr.E2E.Test.App/Startup.cs index 19de79714..56a4358a6 100644 --- a/test/Dapr.E2E.Test.App/Startup.cs +++ b/test/Dapr.E2E.Test.App/Startup.cs @@ -11,158 +11,157 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using Dapr.E2E.Test.Actors.Reminders; +using Dapr.E2E.Test.Actors.Timers; +using Dapr.E2E.Test.Actors.State; +using Dapr.E2E.Test.Actors.ExceptionTesting; +using Dapr.E2E.Test.Actors.Serialization; +using Dapr.E2E.Test.Actors.WeaklyTypedTesting; +using Dapr.E2E.Test.App.ErrorTesting; +using Dapr.Workflow; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Serilog; +using Grpc.Net.Client; + +/// +/// Startup class. +/// +public class Startup { - using Dapr.E2E.Test.Actors.Reminders; - using Dapr.E2E.Test.Actors.Timers; - using Dapr.E2E.Test.Actors.State; - using Dapr.E2E.Test.Actors.ExceptionTesting; - using Dapr.E2E.Test.Actors.Serialization; - using Dapr.E2E.Test.Actors.WeaklyTypedTesting; - using Dapr.E2E.Test.App.ErrorTesting; - using Dapr.Workflow; - using Microsoft.AspNetCore.Authentication; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - using System.Threading.Tasks; - using Microsoft.Extensions.Logging; - using Serilog; - using Grpc.Net.Client; + bool JsonSerializationEnabled => + System.Linq.Enumerable.Contains(System.Environment.GetCommandLineArgs(), "--json-serialization"); /// - /// Startup class. + /// Initializes a new instance of the class. /// - public class Startup + /// Configuration. + public Startup(IConfiguration configuration) { - bool JsonSerializationEnabled => - System.Linq.Enumerable.Contains(System.Environment.GetCommandLineArgs(), "--json-serialization"); - - /// - /// Initializes a new instance of the class. - /// - /// Configuration. - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } - /// - /// Gets the configuration. - /// - public IConfiguration Configuration { get; } + /// + /// Gets the configuration. + /// + public IConfiguration Configuration { get; } - /// - /// Configures Services. - /// - /// Service Collection. - public void ConfigureServices(IServiceCollection services) + /// + /// Configures Services. + /// + /// Service Collection. + public void ConfigureServices(IServiceCollection services) + { + services.AddAuthentication().AddDapr(); + services.AddAuthorization(o => o.AddDapr()); + services.AddControllers().AddDapr(); + services.AddLogging(builder => + { + builder.AddConsole(); + }); + // Register a workflow and associated activity + services.AddDaprWorkflow(options => { - services.AddAuthentication().AddDapr(); - services.AddAuthorization(o => o.AddDapr()); - services.AddControllers().AddDapr(); - services.AddLogging(builder => + // Example of registering a "PlaceOrder" workflow function + options.RegisterWorkflow("PlaceOrder", implementation: async (context, input) => { - builder.AddConsole(); + + var itemToPurchase = input; + + // There are 5 of the same event to test that multiple similarly-named events can be raised in parallel + await context.WaitForExternalEventAsync("ChangePurchaseItem"); + await context.WaitForExternalEventAsync("ChangePurchaseItem"); + await context.WaitForExternalEventAsync("ChangePurchaseItem"); + await context.WaitForExternalEventAsync("ChangePurchaseItem"); + itemToPurchase = await context.WaitForExternalEventAsync("ChangePurchaseItem"); + + // In real life there are other steps related to placing an order, like reserving + // inventory and charging the customer credit card etc. But let's keep it simple ;) + await context.CallActivityAsync("ShipProduct", itemToPurchase); + + return itemToPurchase; }); - // Register a workflow and associated activity - services.AddDaprWorkflow(options => + + // Example of registering a "ShipProduct" workflow activity function + options.RegisterActivity("ShipProduct", implementation: (context, input) => { - // Example of registering a "PlaceOrder" workflow function - options.RegisterWorkflow("PlaceOrder", implementation: async (context, input) => - { - - var itemToPurchase = input; - - // There are 5 of the same event to test that multiple similarly-named events can be raised in parallel - await context.WaitForExternalEventAsync("ChangePurchaseItem"); - await context.WaitForExternalEventAsync("ChangePurchaseItem"); - await context.WaitForExternalEventAsync("ChangePurchaseItem"); - await context.WaitForExternalEventAsync("ChangePurchaseItem"); - itemToPurchase = await context.WaitForExternalEventAsync("ChangePurchaseItem"); - - // In real life there are other steps related to placing an order, like reserving - // inventory and charging the customer credit card etc. But let's keep it simple ;) - await context.CallActivityAsync("ShipProduct", itemToPurchase); - - return itemToPurchase; - }); - - // Example of registering a "ShipProduct" workflow activity function - options.RegisterActivity("ShipProduct", implementation: (context, input) => - { - return Task.FromResult($"We are shipping {input} to the customer using our hoard of drones!"); - }); + return Task.FromResult($"We are shipping {input} to the customer using our hoard of drones!"); }); - services.AddDaprWorkflow(options => + }); + services.AddDaprWorkflow(options => + { + // Example of registering a "StartOrder" workflow function + options.RegisterWorkflow("StartLargeOrder", implementation: async (context, input) => { - // Example of registering a "StartOrder" workflow function - options.RegisterWorkflow("StartLargeOrder", implementation: async (context, input) => - { - var itemToPurchase = input; - itemToPurchase = await context.WaitForExternalEventAsync("FinishLargeOrder"); - return itemToPurchase; - }); - options.RegisterActivity("FinishLargeOrder", implementation: (context, input) => - { - return Task.FromResult($"We are finishing, it's huge!"); - }); - options.UseGrpcChannelOptions(new GrpcChannelOptions - { - MaxReceiveMessageSize = 32 * 1024 * 1024, - MaxSendMessageSize = 32 * 1024 * 1024 - }); + var itemToPurchase = input; + itemToPurchase = await context.WaitForExternalEventAsync("FinishLargeOrder"); + return itemToPurchase; }); - services.AddActors(options => + options.RegisterActivity("FinishLargeOrder", implementation: (context, input) => { - options.UseJsonSerialization = JsonSerializationEnabled; - options.Actors.RegisterActor(); - options.Actors.RegisterActor(); - options.Actors.RegisterActor(); - options.Actors.RegisterActor(); - options.Actors.RegisterActor(); - options.Actors.RegisterActor(); - options.Actors.RegisterActor(); + return Task.FromResult($"We are finishing, it's huge!"); }); - } - - /// - /// Configures Application Builder and WebHost environment. - /// - /// Application builder. - /// Webhost environment. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - var logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); - - LoggerFactory.Create(builder => + options.UseGrpcChannelOptions(new GrpcChannelOptions { - builder.AddSerilog(logger); + MaxReceiveMessageSize = 32 * 1024 * 1024, + MaxSendMessageSize = 32 * 1024 * 1024 }); - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + }); + services.AddActors(options => + { + options.UseJsonSerialization = JsonSerializationEnabled; + options.Actors.RegisterActor(); + options.Actors.RegisterActor(); + options.Actors.RegisterActor(); + options.Actors.RegisterActor(); + options.Actors.RegisterActor(); + options.Actors.RegisterActor(); + options.Actors.RegisterActor(); + }); + } - app.UseSerilogRequestLogging(); + /// + /// Configures Application Builder and WebHost environment. + /// + /// Application builder. + /// Webhost environment. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + var logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); - app.UseRouting(); + LoggerFactory.Create(builder => + { + builder.AddSerilog(logger); + }); + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } - app.UseAuthentication(); + app.UseSerilogRequestLogging(); - app.UseAuthorization(); + app.UseRouting(); - app.UseCloudEvents(); + app.UseAuthentication(); - app.UseEndpoints(endpoints => - { - endpoints.MapSubscribeHandler(); - endpoints.MapActorsHandlers(); - endpoints.MapControllers(); - }); - } + app.UseAuthorization(); + + app.UseCloudEvents(); + + app.UseEndpoints(endpoints => + { + endpoints.MapSubscribeHandler(); + endpoints.MapActorsHandlers(); + endpoints.MapControllers(); + }); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test.App/Transaction.cs b/test/Dapr.E2E.Test.App/Transaction.cs index a24c818ce..f7978f6bd 100644 --- a/test/Dapr.E2E.Test.App/Transaction.cs +++ b/test/Dapr.E2E.Test.App/Transaction.cs @@ -11,25 +11,24 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test -{ - using System.ComponentModel.DataAnnotations; +namespace Dapr.E2E.Test; + +using System.ComponentModel.DataAnnotations; +/// +/// Represents a transaction used by sample code. +/// +public class Transaction +{ /// - /// Represents a transaction used by sample code. + /// Gets or sets account id for the transaction. /// - public class Transaction - { - /// - /// Gets or sets account id for the transaction. - /// - [Required] - public string Id { get; set; } + [Required] + public string Id { get; set; } - /// - /// Gets or sets amount for the transaction. - /// - [Range(0, double.MaxValue)] - public decimal Amount { get; set; } - } + /// + /// Gets or sets amount for the transaction. + /// + [Range(0, double.MaxValue)] + public decimal Amount { get; set; } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test/AVeryCoolWorkAroundTest.cs b/test/Dapr.E2E.Test/AVeryCoolWorkAroundTest.cs index b7fd4ebd1..30286ac8a 100644 --- a/test/Dapr.E2E.Test/AVeryCoolWorkAroundTest.cs +++ b/test/Dapr.E2E.Test/AVeryCoolWorkAroundTest.cs @@ -13,16 +13,15 @@ // This test has been added as a workaround for https://github.com/NasAmin/trx-parser/issues/111 // to report dummy test results in Dapr.E2E.Test.dll // Can delete this once this bug is fixed -namespace Dapr.E2E.WorkAround -{ - using Xunit; +namespace Dapr.E2E.WorkAround; + +using Xunit; - public class WorkAroundTests +public class WorkAroundTests +{ + [Fact] + public void AVeryCoolWorkAroundTest() { - [Fact] - public void AVeryCoolWorkAroundTest() - { - Assert.True(true); - } + Assert.True(true); } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test/ActorRuntimeChecker.cs b/test/Dapr.E2E.Test/ActorRuntimeChecker.cs index 57c46dc93..244e2e8b8 100644 --- a/test/Dapr.E2E.Test/ActorRuntimeChecker.cs +++ b/test/Dapr.E2E.Test/ActorRuntimeChecker.cs @@ -17,28 +17,27 @@ using Dapr.E2E.Test.Actors; using Xunit.Abstractions; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public static class ActorRuntimeChecker { - public static class ActorRuntimeChecker + public static async Task WaitForActorRuntimeAsync(string appId, ITestOutputHelper output, IPingActor proxy, CancellationToken cancellationToken) { - public static async Task WaitForActorRuntimeAsync(string appId, ITestOutputHelper output, IPingActor proxy, CancellationToken cancellationToken) + while (true) { - while (true) - { - output.WriteLine($"Waiting for actor to be ready in: {appId}"); - cancellationToken.ThrowIfCancellationRequested(); + output.WriteLine($"Waiting for actor to be ready in: {appId}"); + cancellationToken.ThrowIfCancellationRequested(); - try - { - await proxy.Ping(); - output.WriteLine($"Found actor in: {appId}"); - break; - } - catch (DaprApiException) - { - await Task.Delay(TimeSpan.FromMilliseconds(250)); - } + try + { + await proxy.Ping(); + output.WriteLine($"Found actor in: {appId}"); + break; + } + catch (DaprApiException) + { + await Task.Delay(TimeSpan.FromMilliseconds(250)); } } } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs index 26f01fb5a..d0f12315a 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs @@ -10,106 +10,105 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using System; +using System.Diagnostics; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.Actors.Client; +using Dapr.E2E.Test.Actors; +using Xunit; +using Xunit.Abstractions; + +public class CustomSerializerTests : DaprTestAppLifecycle { - using System; - using System.Diagnostics; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.Actors.Client; - using Dapr.E2E.Test.Actors; - using Xunit; - using Xunit.Abstractions; + private readonly Lazy proxyFactory; + private IActorProxyFactory ProxyFactory => this.HttpEndpoint == null ? null : this.proxyFactory.Value; - public class CustomSerializerTests : DaprTestAppLifecycle + public CustomSerializerTests(ITestOutputHelper output, DaprTestAppFixture fixture) : base(output, fixture) { - private readonly Lazy proxyFactory; - private IActorProxyFactory ProxyFactory => this.HttpEndpoint == null ? null : this.proxyFactory.Value; - - public CustomSerializerTests(ITestOutputHelper output, DaprTestAppFixture fixture) : base(output, fixture) + base.Configuration = new DaprRunConfiguration { - base.Configuration = new DaprRunConfiguration - { - UseAppPort = true, - AppId = "serializerapp", - AppJsonSerialization = true, - TargetProject = "./../../../../../test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj" - }; + UseAppPort = true, + AppId = "serializerapp", + AppJsonSerialization = true, + TargetProject = "./../../../../../test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj" + }; - this.proxyFactory = new Lazy(() => - { - Debug.Assert(this.HttpEndpoint != null); - return new ActorProxyFactory(new ActorProxyOptions() { - HttpEndpoint = this.HttpEndpoint, - JsonSerializerOptions = new JsonSerializerOptions() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true, - WriteIndented = true, - }, - UseJsonSerialization = true, - }); + this.proxyFactory = new Lazy(() => + { + Debug.Assert(this.HttpEndpoint != null); + return new ActorProxyFactory(new ActorProxyOptions() { + HttpEndpoint = this.HttpEndpoint, + JsonSerializerOptions = new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + WriteIndented = true, + }, + UseJsonSerialization = true, }); - } + }); + } - [Fact] - public async Task ActorCanSupportCustomSerializer() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "SerializationActor"); + [Fact] + public async Task ActorCanSupportCustomSerializer() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "SerializationActor"); - await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cts.Token); + await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cts.Token); - var payload = new SerializationPayload("hello world") + var payload = new SerializationPayload("hello world") + { + Value = JsonSerializer.SerializeToElement(new { foo = "bar" }), + ExtensionData = new System.Collections.Generic.Dictionary() { - Value = JsonSerializer.SerializeToElement(new { foo = "bar" }), - ExtensionData = new System.Collections.Generic.Dictionary() - { - { "baz", "qux" }, - { "count", 42 }, - } - }; + { "baz", "qux" }, + { "count", 42 }, + } + }; - var result = await proxy.SendAsync("test", payload, CancellationToken.None); + var result = await proxy.SendAsync("test", payload, CancellationToken.None); - Assert.Equal(payload.Message, result.Message); - Assert.Equal(payload.Value.GetRawText(), result.Value.GetRawText()); - Assert.Equal(payload.ExtensionData.Count, result.ExtensionData.Count); + Assert.Equal(payload.Message, result.Message); + Assert.Equal(payload.Value.GetRawText(), result.Value.GetRawText()); + Assert.Equal(payload.ExtensionData.Count, result.ExtensionData.Count); - foreach (var kvp in payload.ExtensionData) - { - Assert.True(result.ExtensionData.TryGetValue(kvp.Key, out var value)); - Assert.Equal(JsonSerializer.Serialize(kvp.Value), JsonSerializer.Serialize(value)); - } + foreach (var kvp in payload.ExtensionData) + { + Assert.True(result.ExtensionData.TryGetValue(kvp.Key, out var value)); + Assert.Equal(JsonSerializer.Serialize(kvp.Value), JsonSerializer.Serialize(value)); } + } - /// - /// This was actually a problem that is why the test exists. - /// It just checks, if the interface of the actor has more than one method defined, - /// that if can call it and serialize the payload correctly. - /// - /// - /// More than one methods means here, that in the exact interface must be two methods defined. - /// That excludes hirachies. - /// So wouldn't count here, because it's not directly defined in - /// . (it's defined in the base of it.) - /// That why was created, - /// so there are now more then one method. - /// - [Fact] - public async Task ActorCanSupportCustomSerializerAndCallMoreThenOneDefinedMethod() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "SerializationActor"); + /// + /// This was actually a problem that is why the test exists. + /// It just checks, if the interface of the actor has more than one method defined, + /// that if can call it and serialize the payload correctly. + /// + /// + /// More than one methods means here, that in the exact interface must be two methods defined. + /// That excludes hirachies. + /// So wouldn't count here, because it's not directly defined in + /// . (it's defined in the base of it.) + /// That why was created, + /// so there are now more then one method. + /// + [Fact] + public async Task ActorCanSupportCustomSerializerAndCallMoreThenOneDefinedMethod() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "SerializationActor"); - await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cts.Token); + await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cts.Token); - var payload = DateTime.MinValue; - var result = await proxy.AnotherMethod(payload); + var payload = DateTime.MinValue; + var result = await proxy.AnotherMethod(payload); - Assert.Equal(payload, result); - } + Assert.Equal(payload, result); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs index fc036d15d..ee4645dca 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs @@ -11,27 +11,26 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.E2E.Test.Actors.ExceptionTesting; +using Xunit; +public partial class E2ETests : IAsyncLifetime { - using System; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.E2E.Test.Actors.ExceptionTesting; - using Xunit; - public partial class E2ETests : IAsyncLifetime + [Fact] + public async Task ActorCanProvideExceptionDetails() { - [Fact] - public async Task ActorCanProvideExceptionDetails() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ExceptionActor"); - await WaitForActorRuntimeAsync(proxy, cts.Token); - ActorMethodInvocationException ex = await Assert.ThrowsAsync(async () => await proxy.ExceptionExample()); - Assert.Contains("Remote Actor Method Exception", ex.Message); - Assert.Contains("ExceptionExample", ex.Message); - Assert.Contains("32", ex.Message); - } + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ExceptionActor"); + await WaitForActorRuntimeAsync(proxy, cts.Token); + ActorMethodInvocationException ex = await Assert.ThrowsAsync(async () => await proxy.ExceptionExample()); + Assert.Contains("Remote Actor Method Exception", ex.Message); + Assert.Contains("ExceptionExample", ex.Message); + Assert.Contains("32", ex.Message); } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.ReentrantTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.ReentrantTests.cs index 30b19a450..3b31e766f 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.ReentrantTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.ReentrantTests.cs @@ -10,70 +10,69 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.Actors.Client; +using Dapr.E2E.Test.Actors.Reentrancy; +using Xunit; +using Xunit.Abstractions; + +public class ReentrantTests : DaprTestAppLifecycle { - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.Actors.Client; - using Dapr.E2E.Test.Actors.Reentrancy; - using Xunit; - using Xunit.Abstractions; + private static readonly int NumCalls = 10; + private readonly Lazy proxyFactory; + private IActorProxyFactory ProxyFactory => this.HttpEndpoint == null ? null : this.proxyFactory.Value; - public class ReentrantTests : DaprTestAppLifecycle + public ReentrantTests(ITestOutputHelper output, DaprTestAppFixture fixture) : base(output, fixture) { - private static readonly int NumCalls = 10; - private readonly Lazy proxyFactory; - private IActorProxyFactory ProxyFactory => this.HttpEndpoint == null ? null : this.proxyFactory.Value; - - public ReentrantTests(ITestOutputHelper output, DaprTestAppFixture fixture) : base(output, fixture) + base.Configuration = new DaprRunConfiguration { - base.Configuration = new DaprRunConfiguration - { - UseAppPort = true, - AppId = "reentrantapp", - TargetProject = "./../../../../../test/Dapr.E2E.Test.App.ReentrantActor/Dapr.E2E.Test.App.ReentrantActors.csproj", - }; + UseAppPort = true, + AppId = "reentrantapp", + TargetProject = "./../../../../../test/Dapr.E2E.Test.App.ReentrantActor/Dapr.E2E.Test.App.ReentrantActors.csproj", + }; - this.proxyFactory = new Lazy(() => - { - Debug.Assert(this.HttpEndpoint != null); - return new ActorProxyFactory(new ActorProxyOptions() { HttpEndpoint = this.HttpEndpoint, }); - }); - } - - [Fact] - public async Task ActorCanPerformReentrantCalls() + this.proxyFactory = new Lazy(() => { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReentrantActor"); + Debug.Assert(this.HttpEndpoint != null); + return new ActorProxyFactory(new ActorProxyOptions() { HttpEndpoint = this.HttpEndpoint, }); + }); + } - await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cts.Token); + [Fact] + public async Task ActorCanPerformReentrantCalls() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReentrantActor"); - await proxy.ReentrantCall(new ReentrantCallOptions(){ CallsRemaining = NumCalls, }); - var records = new List(); - for (int i = 0; i < NumCalls; i++) - { - var state = await proxy.GetState(i); - records.AddRange(state.Records); - } + await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cts.Token); - var enterRecords = records.FindAll(record => record.IsEnter); - var exitRecords = records.FindAll(record => !record.IsEnter); + await proxy.ReentrantCall(new ReentrantCallOptions(){ CallsRemaining = NumCalls, }); + var records = new List(); + for (int i = 0; i < NumCalls; i++) + { + var state = await proxy.GetState(i); + records.AddRange(state.Records); + } + + var enterRecords = records.FindAll(record => record.IsEnter); + var exitRecords = records.FindAll(record => !record.IsEnter); - this.Output.WriteLine($"Got {records.Count} records."); - Assert.True(records.Count == NumCalls * 2); - for (int i = 0; i < NumCalls; i++) + this.Output.WriteLine($"Got {records.Count} records."); + Assert.True(records.Count == NumCalls * 2); + for (int i = 0; i < NumCalls; i++) + { + for (int j = 0; j < NumCalls; j++) { - for (int j = 0; j < NumCalls; j++) - { - // Assert all the enters happen before the exits. - Assert.True(enterRecords[i].Timestamp < exitRecords[j].Timestamp); - } + // Assert all the enters happen before the exits. + Assert.True(enterRecords[i].Timestamp < exitRecords[j].Timestamp); } } } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.Regression762Tests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.Regression762Tests.cs index 12d6c7365..c4aa669b7 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.Regression762Tests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.Regression762Tests.cs @@ -18,99 +18,98 @@ using Dapr.E2E.Test.Actors.ErrorTesting; using Xunit; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public partial class E2ETests : IAsyncLifetime { - public partial class E2ETests : IAsyncLifetime + [Fact] + public async Task ActorSuccessfullyClearsStateAfterErrorWithRemoting() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "Regression762Actor"); + + await WaitForActorRuntimeAsync(proxy, cts.Token); + + var key = Guid.NewGuid().ToString(); + var throwingCall = new StateCall + { + Key = key, + Value = "Throw value", + Operation = "ThrowException" + }; + + var setCall = new StateCall() + { + Key = key, + Value = "Real value", + Operation = "SetState" + }; + + var savingCall = new StateCall() + { + Operation = "SaveState" + }; + + // We attempt to delete it on the unlikely chance it's already there. + await proxy.RemoveState(throwingCall.Key); + + // Initiate a call that will set the state, then throw. + await Assert.ThrowsAsync(async () => await proxy.SaveState(throwingCall)); + + // Save the state and assert that the old value was not persisted. + await proxy.SaveState(savingCall); + var errorResp = await proxy.GetState(key); + Assert.Equal(string.Empty, errorResp); + + // Persist normally and ensure it works. + await proxy.SaveState(setCall); + var resp = await proxy.GetState(key); + Assert.Equal("Real value", resp); + } + + [Fact] + public async Task ActorSuccessfullyClearsStateAfterErrorWithoutRemoting() { - [Fact] - public async Task ActorSuccessfullyClearsStateAfterErrorWithRemoting() + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var pingProxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "Regression762Actor"); + var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "Regression762Actor"); + + await WaitForActorRuntimeAsync(pingProxy, cts.Token); + + var key = Guid.NewGuid().ToString(); + var throwingCall = new StateCall { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "Regression762Actor"); - - await WaitForActorRuntimeAsync(proxy, cts.Token); - - var key = Guid.NewGuid().ToString(); - var throwingCall = new StateCall - { - Key = key, - Value = "Throw value", - Operation = "ThrowException" - }; - - var setCall = new StateCall() - { - Key = key, - Value = "Real value", - Operation = "SetState" - }; - - var savingCall = new StateCall() - { - Operation = "SaveState" - }; - - // We attempt to delete it on the unlikely chance it's already there. - await proxy.RemoveState(throwingCall.Key); - - // Initiate a call that will set the state, then throw. - await Assert.ThrowsAsync(async () => await proxy.SaveState(throwingCall)); - - // Save the state and assert that the old value was not persisted. - await proxy.SaveState(savingCall); - var errorResp = await proxy.GetState(key); - Assert.Equal(string.Empty, errorResp); - - // Persist normally and ensure it works. - await proxy.SaveState(setCall); - var resp = await proxy.GetState(key); - Assert.Equal("Real value", resp); - } - - [Fact] - public async Task ActorSuccessfullyClearsStateAfterErrorWithoutRemoting() + Key = key, + Value = "Throw value", + Operation = "ThrowException" + }; + + var setCall = new StateCall() + { + Key = key, + Value = "Real value", + Operation = "SetState" + }; + + var savingCall = new StateCall() { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var pingProxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "Regression762Actor"); - var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "Regression762Actor"); - - await WaitForActorRuntimeAsync(pingProxy, cts.Token); - - var key = Guid.NewGuid().ToString(); - var throwingCall = new StateCall - { - Key = key, - Value = "Throw value", - Operation = "ThrowException" - }; - - var setCall = new StateCall() - { - Key = key, - Value = "Real value", - Operation = "SetState" - }; - - var savingCall = new StateCall() - { - Operation = "SaveState" - }; - - // We attempt to delete it on the unlikely chance it's already there. - await proxy.InvokeMethodAsync("RemoveState", throwingCall.Key); - - // Initiate a call that will set the state, then throw. - await Assert.ThrowsAsync(async () => await proxy.InvokeMethodAsync("SaveState", throwingCall)); + Operation = "SaveState" + }; + + // We attempt to delete it on the unlikely chance it's already there. + await proxy.InvokeMethodAsync("RemoveState", throwingCall.Key); + + // Initiate a call that will set the state, then throw. + await Assert.ThrowsAsync(async () => await proxy.InvokeMethodAsync("SaveState", throwingCall)); - // Save the state and assert that the old value was not persisted. - await proxy.InvokeMethodAsync("SaveState", savingCall); - var errorResp = await proxy.InvokeMethodAsync("GetState", key); - Assert.Equal(string.Empty, errorResp); - - // Persist normally and ensure it works. - await proxy.InvokeMethodAsync("SaveState", setCall); - var resp = await proxy.InvokeMethodAsync("GetState", key); - Assert.Equal("Real value", resp); - } + // Save the state and assert that the old value was not persisted. + await proxy.InvokeMethodAsync("SaveState", savingCall); + var errorResp = await proxy.InvokeMethodAsync("GetState", key); + Assert.Equal(string.Empty, errorResp); + + // Persist normally and ensure it works. + await proxy.InvokeMethodAsync("SaveState", setCall); + var resp = await proxy.InvokeMethodAsync("GetState", key); + Assert.Equal("Real value", resp); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.ReminderTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.ReminderTests.cs index ff39cce8a..378d96d26 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.ReminderTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.ReminderTests.cs @@ -10,170 +10,169 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.E2E.Test.Actors.Reminders; +using Xunit; + +public partial class E2ETests : IAsyncLifetime { - using System; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.E2E.Test.Actors.Reminders; - using Xunit; - - public partial class E2ETests : IAsyncLifetime + [Fact] + public async Task ActorCanStartAndStopReminder() { - [Fact] - public async Task ActorCanStartAndStopReminder() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); - await WaitForActorRuntimeAsync(proxy, cts.Token); + await WaitForActorRuntimeAsync(proxy, cts.Token); - // Start reminder, to count up to 10 - await proxy.StartReminder(new StartReminderOptions(){ Total = 10, }); + // Start reminder, to count up to 10 + await proxy.StartReminder(new StartReminderOptions(){ Total = 10, }); - State state; - while (true) + State state; + while (true) + { + cts.Token.ThrowIfCancellationRequested(); + + state = await proxy.GetState(); + this.Output.WriteLine($"Got Count: {state.Count} IsReminderRunning: {state.IsReminderRunning}"); + if (!state.IsReminderRunning) { - cts.Token.ThrowIfCancellationRequested(); - - state = await proxy.GetState(); - this.Output.WriteLine($"Got Count: {state.Count} IsReminderRunning: {state.IsReminderRunning}"); - if (!state.IsReminderRunning) - { - break; - } + break; } - - // Should count up to exactly 10 - Assert.Equal(10, state.Count); } - [Fact] - public async Task ActorCanStartAndStopAndGetReminder() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); + // Should count up to exactly 10 + Assert.Equal(10, state.Count); + } + + [Fact] + public async Task ActorCanStartAndStopAndGetReminder() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); - await WaitForActorRuntimeAsync(proxy, cts.Token); + await WaitForActorRuntimeAsync(proxy, cts.Token); - // Get reminder before starting it, should return null. - var reminder = await proxy.GetReminder(); - Assert.Equal("null", reminder); + // Get reminder before starting it, should return null. + var reminder = await proxy.GetReminder(); + Assert.Equal("null", reminder); - // Start reminder, to count up to 10 - await proxy.StartReminder(new StartReminderOptions(){ Total = 10, }); + // Start reminder, to count up to 10 + await proxy.StartReminder(new StartReminderOptions(){ Total = 10, }); + + State state = new State(); + var countGetReminder = 0; + while (true) + { + cts.Token.ThrowIfCancellationRequested(); + + reminder = await proxy.GetReminder(); + Assert.NotNull(reminder); + + // If reminder is null then it means the reminder has been stopped. + if (reminder != "null") + { + countGetReminder++; + var reminderJson = JsonSerializer.Deserialize(reminder); + var name = reminderJson.GetProperty("name").ToString(); + var period = reminderJson.GetProperty("period").ToString(); + var dueTime = reminderJson.GetProperty("dueTime").ToString(); + + Assert.Equal("test-reminder", name); + Assert.Equal(TimeSpan.FromMilliseconds(50).ToString(), period); + Assert.Equal(TimeSpan.Zero.ToString(), dueTime); + } - State state = new State(); - var countGetReminder = 0; - while (true) + state = await proxy.GetState(); + this.Output.WriteLine($"Got Count: {state.Count} IsReminderRunning: {state.IsReminderRunning}"); + if (!state.IsReminderRunning) { - cts.Token.ThrowIfCancellationRequested(); - - reminder = await proxy.GetReminder(); - Assert.NotNull(reminder); - - // If reminder is null then it means the reminder has been stopped. - if (reminder != "null") - { - countGetReminder++; - var reminderJson = JsonSerializer.Deserialize(reminder); - var name = reminderJson.GetProperty("name").ToString(); - var period = reminderJson.GetProperty("period").ToString(); - var dueTime = reminderJson.GetProperty("dueTime").ToString(); - - Assert.Equal("test-reminder", name); - Assert.Equal(TimeSpan.FromMilliseconds(50).ToString(), period); - Assert.Equal(TimeSpan.Zero.ToString(), dueTime); - } - - state = await proxy.GetState(); - this.Output.WriteLine($"Got Count: {state.Count} IsReminderRunning: {state.IsReminderRunning}"); - if (!state.IsReminderRunning) - { - break; - } + break; } + } - // Should count up to exactly 10 - Assert.Equal(10, state.Count); - // Should be able to Get Reminder at least once. - Assert.True(countGetReminder > 0); - } + // Should count up to exactly 10 + Assert.Equal(10, state.Count); + // Should be able to Get Reminder at least once. + Assert.True(countGetReminder > 0); + } - [Fact] - public async Task ActorCanStartReminderWithRepetitions() - { - int repetitions = 5; - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); + [Fact] + public async Task ActorCanStartReminderWithRepetitions() + { + int repetitions = 5; + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); - await WaitForActorRuntimeAsync(proxy, cts.Token); + await WaitForActorRuntimeAsync(proxy, cts.Token); - // Reminder that should fire 5 times (repetitions) at an interval of 1s - await proxy.StartReminderWithRepetitions(repetitions); - var start = DateTime.Now; + // Reminder that should fire 5 times (repetitions) at an interval of 1s + await proxy.StartReminderWithRepetitions(repetitions); + var start = DateTime.Now; - await Task.Delay(TimeSpan.FromSeconds(7)); + await Task.Delay(TimeSpan.FromSeconds(7)); - var state = await proxy.GetState(); + var state = await proxy.GetState(); - // Make sure the reminder fired 5 times (repetitions) - Assert.Equal(repetitions, state.Count); + // Make sure the reminder fired 5 times (repetitions) + Assert.Equal(repetitions, state.Count); - // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. - Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Reminder may not have triggered."); - Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), - $"Reminder triggered too recently. {DateTime.Now} - {state.Timestamp}"); - } + // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. + Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Reminder may not have triggered."); + Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), + $"Reminder triggered too recently. {DateTime.Now} - {state.Timestamp}"); + } - [Fact] - public async Task ActorCanStartReminderWithTtlAndRepetitions() - { - int repetitions = 2; - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); + [Fact] + public async Task ActorCanStartReminderWithTtlAndRepetitions() + { + int repetitions = 2; + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); - await WaitForActorRuntimeAsync(proxy, cts.Token); + await WaitForActorRuntimeAsync(proxy, cts.Token); - // Reminder that should fire 2 times (repetitions) at an interval of 1s - await proxy.StartReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), repetitions); - var start = DateTime.Now; + // Reminder that should fire 2 times (repetitions) at an interval of 1s + await proxy.StartReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), repetitions); + var start = DateTime.Now; - await Task.Delay(TimeSpan.FromSeconds(5)); + await Task.Delay(TimeSpan.FromSeconds(5)); - var state = await proxy.GetState(); + var state = await proxy.GetState(); - // Make sure the reminder fired 2 times (repetitions) whereas the ttl was 5 seconds. - Assert.Equal(repetitions, state.Count); + // Make sure the reminder fired 2 times (repetitions) whereas the ttl was 5 seconds. + Assert.Equal(repetitions, state.Count); - // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. - Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Reminder may not have triggered."); - Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), - $"Reminder triggered too recently. {DateTime.Now} - {state.Timestamp}"); - } + // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. + Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Reminder may not have triggered."); + Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), + $"Reminder triggered too recently. {DateTime.Now} - {state.Timestamp}"); + } - [Fact] - public async Task ActorCanStartReminderWithTtl() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); + [Fact] + public async Task ActorCanStartReminderWithTtl() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ReminderActor"); - await WaitForActorRuntimeAsync(proxy, cts.Token); + await WaitForActorRuntimeAsync(proxy, cts.Token); - // Reminder that should fire 3 times (at 0, 1, and 2 seconds) - await proxy.StartReminderWithTtl(TimeSpan.FromSeconds(2)); + // Reminder that should fire 3 times (at 0, 1, and 2 seconds) + await proxy.StartReminderWithTtl(TimeSpan.FromSeconds(2)); - // Record the start time and wait for longer than the reminder should exist for. - var start = DateTime.Now; - await Task.Delay(TimeSpan.FromSeconds(5)); + // Record the start time and wait for longer than the reminder should exist for. + var start = DateTime.Now; + await Task.Delay(TimeSpan.FromSeconds(5)); - var state = await proxy.GetState(); + var state = await proxy.GetState(); - // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. - Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Reminder may not have triggered."); - Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), $"Reminder triggered too recently. {DateTime.Now} - {state.Timestamp}"); - } + // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. + Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Reminder may not have triggered."); + Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), $"Reminder triggered too recently. {DateTime.Now} - {state.Timestamp}"); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs index 7a1408a0e..6c2a5023c 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs @@ -10,113 +10,112 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.E2E.Test.Actors.State; +using Xunit; + +public partial class E2ETests : IAsyncLifetime { - using System; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.E2E.Test.Actors.State; - using Xunit; - - public partial class E2ETests : IAsyncLifetime + [Fact] + public async Task ActorCanSaveStateWithTTL() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "StateActor"); + + await WaitForActorRuntimeAsync(proxy, cts.Token); + + await proxy.SetState("key", "value", TimeSpan.FromSeconds(2)); + + var resp = await proxy.GetState("key"); + Assert.Equal("value", resp); + + await Task.Delay(TimeSpan.FromSeconds(2.5)); + + // Assert key no longer exists. + await Assert.ThrowsAsync(() => proxy.GetState("key")); + + // Can create key again + await proxy.SetState("key", "new-value", null); + resp = await proxy.GetState("key"); + Assert.Equal("new-value", resp); + } + + [Fact] + public async Task ActorStateTTLOverridesExisting() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "StateActor"); + + await WaitForActorRuntimeAsync(proxy, cts.Token); + + // TLL 4 seconds + await proxy.SetState("key", "value", TimeSpan.FromSeconds(4)); + + var resp = await proxy.GetState("key"); + Assert.Equal("value", resp); + + // TLL 2 seconds + await Task.Delay(TimeSpan.FromSeconds(2)); + resp = await proxy.GetState("key"); + Assert.Equal("value", resp); + + // TLL 4 seconds + await proxy.SetState("key", "value", TimeSpan.FromSeconds(4)); + + // TLL 2 seconds + await Task.Delay(TimeSpan.FromSeconds(2)); + resp = await proxy.GetState("key"); + Assert.Equal("value", resp); + + // TLL 0 seconds + await Task.Delay(TimeSpan.FromSeconds(2.5)); + + // Assert key no longer exists. + await Assert.ThrowsAsync(() => proxy.GetState("key")); + } + + [Fact] + public async Task ActorStateTTLRemoveTTL() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "StateActor"); + + await WaitForActorRuntimeAsync(proxy, cts.Token); + + // Can remove TTL and then add again + await proxy.SetState("key", "value", TimeSpan.FromSeconds(2)); + await proxy.SetState("key", "value", null); + await Task.Delay(TimeSpan.FromSeconds(2)); + var resp = await proxy.GetState("key"); + Assert.Equal("value", resp); + await proxy.SetState("key", "value", TimeSpan.FromSeconds(2)); + await Task.Delay(TimeSpan.FromSeconds(2.5)); + await Assert.ThrowsAsync(() => proxy.GetState("key")); + } + + [Fact] + public async Task ActorStateBetweenProxies() { - [Fact] - public async Task ActorCanSaveStateWithTTL() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "StateActor"); - - await WaitForActorRuntimeAsync(proxy, cts.Token); - - await proxy.SetState("key", "value", TimeSpan.FromSeconds(2)); - - var resp = await proxy.GetState("key"); - Assert.Equal("value", resp); - - await Task.Delay(TimeSpan.FromSeconds(2.5)); - - // Assert key no longer exists. - await Assert.ThrowsAsync(() => proxy.GetState("key")); - - // Can create key again - await proxy.SetState("key", "new-value", null); - resp = await proxy.GetState("key"); - Assert.Equal("new-value", resp); - } - - [Fact] - public async Task ActorStateTTLOverridesExisting() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "StateActor"); - - await WaitForActorRuntimeAsync(proxy, cts.Token); - - // TLL 4 seconds - await proxy.SetState("key", "value", TimeSpan.FromSeconds(4)); - - var resp = await proxy.GetState("key"); - Assert.Equal("value", resp); - - // TLL 2 seconds - await Task.Delay(TimeSpan.FromSeconds(2)); - resp = await proxy.GetState("key"); - Assert.Equal("value", resp); - - // TLL 4 seconds - await proxy.SetState("key", "value", TimeSpan.FromSeconds(4)); - - // TLL 2 seconds - await Task.Delay(TimeSpan.FromSeconds(2)); - resp = await proxy.GetState("key"); - Assert.Equal("value", resp); - - // TLL 0 seconds - await Task.Delay(TimeSpan.FromSeconds(2.5)); - - // Assert key no longer exists. - await Assert.ThrowsAsync(() => proxy.GetState("key")); - } - - [Fact] - public async Task ActorStateTTLRemoveTTL() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "StateActor"); - - await WaitForActorRuntimeAsync(proxy, cts.Token); - - // Can remove TTL and then add again - await proxy.SetState("key", "value", TimeSpan.FromSeconds(2)); - await proxy.SetState("key", "value", null); - await Task.Delay(TimeSpan.FromSeconds(2)); - var resp = await proxy.GetState("key"); - Assert.Equal("value", resp); - await proxy.SetState("key", "value", TimeSpan.FromSeconds(2)); - await Task.Delay(TimeSpan.FromSeconds(2.5)); - await Assert.ThrowsAsync(() => proxy.GetState("key")); - } - - [Fact] - public async Task ActorStateBetweenProxies() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var actorId = ActorId.CreateRandom(); - var proxy1 = this.ProxyFactory.CreateActorProxy(actorId, "StateActor"); - var proxy2 = this.ProxyFactory.CreateActorProxy(actorId, "StateActor"); - - await WaitForActorRuntimeAsync(proxy1, cts.Token); - - await proxy1.SetState("key", "value", TimeSpan.FromSeconds(2)); - var resp = await proxy1.GetState("key"); - Assert.Equal("value", resp); - resp = await proxy2.GetState("key"); - Assert.Equal("value", resp); - - await Task.Delay(TimeSpan.FromSeconds(2.5)); - await Assert.ThrowsAsync(() => proxy1.GetState("key")); - await Assert.ThrowsAsync(() => proxy2.GetState("key")); - } + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var actorId = ActorId.CreateRandom(); + var proxy1 = this.ProxyFactory.CreateActorProxy(actorId, "StateActor"); + var proxy2 = this.ProxyFactory.CreateActorProxy(actorId, "StateActor"); + + await WaitForActorRuntimeAsync(proxy1, cts.Token); + + await proxy1.SetState("key", "value", TimeSpan.FromSeconds(2)); + var resp = await proxy1.GetState("key"); + Assert.Equal("value", resp); + resp = await proxy2.GetState("key"); + Assert.Equal("value", resp); + + await Task.Delay(TimeSpan.FromSeconds(2.5)); + await Assert.ThrowsAsync(() => proxy1.GetState("key")); + await Assert.ThrowsAsync(() => proxy2.GetState("key")); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.TimerTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.TimerTests.cs index d9aba3863..61e16d8d7 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.TimerTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.TimerTests.cs @@ -10,65 +10,64 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test -{ - using System; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.E2E.Test.Actors.Timers; - using Xunit; +namespace Dapr.E2E.Test; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.E2E.Test.Actors.Timers; +using Xunit; - public partial class E2ETests : IAsyncLifetime +public partial class E2ETests : IAsyncLifetime +{ + [Fact] + public async Task ActorCanStartAndStopTimer() { - [Fact] - public async Task ActorCanStartAndStopTimer() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "TimerActor"); + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "TimerActor"); - await WaitForActorRuntimeAsync(proxy, cts.Token); + await WaitForActorRuntimeAsync(proxy, cts.Token); - // Start timer, to count up to 10 - await proxy.StartTimer(new StartTimerOptions(){ Total = 10, }); + // Start timer, to count up to 10 + await proxy.StartTimer(new StartTimerOptions(){ Total = 10, }); - State state; - while (true) - { - cts.Token.ThrowIfCancellationRequested(); + State state; + while (true) + { + cts.Token.ThrowIfCancellationRequested(); - state = await proxy.GetState(); - this.Output.WriteLine($"Got Count: {state.Count} IsTimerRunning: {state.IsTimerRunning}"); - if (!state.IsTimerRunning) - { - break; - } + state = await proxy.GetState(); + this.Output.WriteLine($"Got Count: {state.Count} IsTimerRunning: {state.IsTimerRunning}"); + if (!state.IsTimerRunning) + { + break; } - - // Should count up to exactly 10 - Assert.Equal(10, state.Count); } - [Fact] - public async Task ActorCanStartTimerWithTtl() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "TimerActor"); + // Should count up to exactly 10 + Assert.Equal(10, state.Count); + } - await WaitForActorRuntimeAsync(proxy, cts.Token); + [Fact] + public async Task ActorCanStartTimerWithTtl() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "TimerActor"); - // Reminder that should fire 3 times (at 0, 1, and 2 seconds) - await proxy.StartTimerWithTtl(TimeSpan.FromSeconds(2)); + await WaitForActorRuntimeAsync(proxy, cts.Token); - // Record the start time and wait for longer than the reminder should exist for. - var start = DateTime.Now; - await Task.Delay(TimeSpan.FromSeconds(5)); + // Reminder that should fire 3 times (at 0, 1, and 2 seconds) + await proxy.StartTimerWithTtl(TimeSpan.FromSeconds(2)); - var state = await proxy.GetState(); + // Record the start time and wait for longer than the reminder should exist for. + var start = DateTime.Now; + await Task.Delay(TimeSpan.FromSeconds(5)); - // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. - Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Timer may not have fired."); - Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), $"Timer fired too recently. {DateTime.Now} - {state.Timestamp}"); - } + var state = await proxy.GetState(); + + // Make sure the reminder has fired and that it didn't fire within the past second since it should have expired. + Assert.True(state.Timestamp.Subtract(start) > TimeSpan.Zero, "Timer may not have fired."); + Assert.True(DateTime.Now.Subtract(state.Timestamp) > TimeSpan.FromSeconds(1), $"Timer fired too recently. {DateTime.Now} - {state.Timestamp}"); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs index 5518f32b8..22040a15d 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs @@ -10,32 +10,32 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test -{ - using System; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.E2E.Test.Actors.WeaklyTypedTesting; - using Shouldly; - using Xunit; +namespace Dapr.E2E.Test; - public partial class E2ETests : IAsyncLifetime - { +using System; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors; +using Dapr.E2E.Test.Actors.WeaklyTypedTesting; +using Shouldly; +using Xunit; + +public partial class E2ETests : IAsyncLifetime +{ #if NET8_0_OR_GREATER - [Fact] - public async Task WeaklyTypedActorCanReturnPolymorphicResponse() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var pingProxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); - var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); + [Fact] + public async Task WeaklyTypedActorCanReturnPolymorphicResponse() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var pingProxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); + var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); - await WaitForActorRuntimeAsync(pingProxy, cts.Token); + await WaitForActorRuntimeAsync(pingProxy, cts.Token); - var result = await proxy.InvokeMethodAsync(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse)); + var result = await proxy.InvokeMethodAsync(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse)); - result.ShouldBeOfType().DerivedProperty.ShouldNotBeNullOrWhiteSpace(); - } + result.ShouldBeOfType().DerivedProperty.ShouldNotBeNullOrWhiteSpace(); + } #else [Fact] public async Task WeaklyTypedActorCanReturnDerivedResponse() @@ -51,18 +51,17 @@ public async Task WeaklyTypedActorCanReturnDerivedResponse() result.ShouldBeOfType().DerivedProperty.ShouldNotBeNullOrWhiteSpace(); } #endif - [Fact] - public async Task WeaklyTypedActorCanReturnNullResponse() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var pingProxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); - var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); + [Fact] + public async Task WeaklyTypedActorCanReturnNullResponse() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + var pingProxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); + var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "WeaklyTypedTestingActor"); - await WaitForActorRuntimeAsync(pingProxy, cts.Token); + await WaitForActorRuntimeAsync(pingProxy, cts.Token); - var result = await proxy.InvokeMethodAsync(nameof(IWeaklyTypedTestingActor.GetNullResponse)); + var result = await proxy.InvokeMethodAsync(nameof(IWeaklyTypedTestingActor.GetNullResponse)); - result.ShouldBeNull(); - } + result.ShouldBeNull(); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/DaprCommand.cs b/test/Dapr.E2E.Test/DaprCommand.cs index 21e31365d..52a369341 100644 --- a/test/Dapr.E2E.Test/DaprCommand.cs +++ b/test/Dapr.E2E.Test/DaprCommand.cs @@ -11,177 +11,176 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Threading; +using Xunit.Abstractions; + +public class DaprCommand { - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Drawing; - using System.Linq; - using System.Threading; - using Xunit.Abstractions; + private readonly ITestOutputHelper output; + private readonly CircularBuffer logBuffer = new CircularBuffer(1000); - public class DaprCommand + public DaprCommand(ITestOutputHelper output) { - private readonly ITestOutputHelper output; - private readonly CircularBuffer logBuffer = new CircularBuffer(1000); - - public DaprCommand(ITestOutputHelper output) - { - this.output = output; - } + this.output = output; + } - private EventWaitHandle outputReceived = new EventWaitHandle(false, EventResetMode.ManualReset); - public string DaprBinaryName { get; set; } - public string Command { get; set; } - public Dictionary EnvironmentVariables { get; set; } = new Dictionary(); - public string[] OutputToMatch { get; set; } - public TimeSpan Timeout { get; set; } + private EventWaitHandle outputReceived = new EventWaitHandle(false, EventResetMode.ManualReset); + public string DaprBinaryName { get; set; } + public string Command { get; set; } + public Dictionary EnvironmentVariables { get; set; } = new Dictionary(); + public string[] OutputToMatch { get; set; } + public TimeSpan Timeout { get; set; } - public void Run() + public void Run() + { + Console.WriteLine($@"Running command: {this.Command}"); + var escapedArgs = Command.Replace("\"", "\\\""); + var process = new Process() { - Console.WriteLine($@"Running command: {this.Command}"); - var escapedArgs = Command.Replace("\"", "\\\""); - var process = new Process() - { - StartInfo = new ProcessStartInfo - { - FileName = this.DaprBinaryName, - Arguments = escapedArgs, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, - } - }; - foreach (var (key, value) in EnvironmentVariables) + StartInfo = new ProcessStartInfo { - process.StartInfo.EnvironmentVariables.Add(key, value); + FileName = this.DaprBinaryName, + Arguments = escapedArgs, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, } - process.OutputDataReceived += CheckOutput; - process.ErrorDataReceived += CheckOutput; + }; + foreach (var (key, value) in EnvironmentVariables) + { + process.StartInfo.EnvironmentVariables.Add(key, value); + } + process.OutputDataReceived += CheckOutput; + process.ErrorDataReceived += CheckOutput; - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); - var done = outputReceived.WaitOne(this.Timeout); - if (!done) - { - var ex = new Exception($"Command: \"{this.Command}\" timed out while waiting for output: \"{this.OutputToMatch}\"{System.Environment.NewLine}" + - "This could also mean the E2E app had a startup error. For more details see the Data property of this exception."); - // we add here the log buffer of the last 1000 lines, of the application log - // to make it easier to debug failing tests - ex.Data.Add("log", this.logBuffer.ToArray()); - throw ex; - } + var done = outputReceived.WaitOne(this.Timeout); + if (!done) + { + var ex = new Exception($"Command: \"{this.Command}\" timed out while waiting for output: \"{this.OutputToMatch}\"{System.Environment.NewLine}" + + "This could also mean the E2E app had a startup error. For more details see the Data property of this exception."); + // we add here the log buffer of the last 1000 lines, of the application log + // to make it easier to debug failing tests + ex.Data.Add("log", this.logBuffer.ToArray()); + throw ex; } + } - private void CheckOutput(object sendingProcess, DataReceivedEventArgs e) + private void CheckOutput(object sendingProcess, DataReceivedEventArgs e) + { + if (e.Data == null) { - if (e.Data == null) - { - return; - } - - try - { - WriteLine(e.Data); - } - catch (InvalidOperationException) - { - } - - if (this.OutputToMatch.Any(o => e.Data.Contains(o))) - { - outputReceived.Set(); - } + return; } - private void OnErrorOutput(object sender, DataReceivedEventArgs e) + try + { + WriteLine(e.Data); + } + catch (InvalidOperationException) { - if (e.Data == null) - { - return; - } - - try - { - WriteLine(e.Data); - } - catch (InvalidOperationException) - { - } } - private void WriteLine(string message) + if (this.OutputToMatch.Any(o => e.Data.Contains(o))) { - // see: https://github.com/xunit/xunit/issues/2146 - var formattedMessage = message.TrimEnd(Environment.NewLine.ToCharArray()); - this.output.WriteLine(formattedMessage); - this.logBuffer.Add(formattedMessage); + outputReceived.Set(); } } - /// - /// A circular buffer that can be used to store a fixed number of items. - /// When the buffer is full, the oldest item is overwritten. - /// The buffer can be read in the same order as the items were added. - /// More information can be found here. - /// - /// - /// The buffer gets initialized by the call to the constructor and will allocate, - /// the memory for the buffer. The buffer is not resizable. - /// That means be carefull with , because it can cause an . - /// - /// The type of what the cicular buffer is off. - internal class CircularBuffer{ - private readonly int size; - private readonly T[] buffer; - private int readPosition = 0; - private int writePosition = 0; - /// - /// Initialize the buffer with the buffer size of . - /// - /// - /// The size the buffer will have - /// - public CircularBuffer(int size) + private void OnErrorOutput(object sender, DataReceivedEventArgs e) + { + if (e.Data == null) { - this.size = size; - buffer = new T[size]; + return; } - /// - /// Adds an item and move the write position to the next value - /// - /// The item that should be written. - public void Add(T item) + + try { - buffer[writePosition] = item; - writePosition = (writePosition + 1) % size; - } - /// - /// Reads on value and move the position to the next value - /// - /// - public T Read(){ - var value = buffer[readPosition]; - readPosition = (readPosition + 1) % size; - return value; + WriteLine(e.Data); } - /// - /// Read the full buffer. - /// While the buffer is read, the read position is moved to the next value - /// - /// - public T[] ToArray() + catch (InvalidOperationException) { - var result = new T[size]; - for (int i = 0; i < size; i++) - { - result[i] = Read(); - } - return result; } } + + private void WriteLine(string message) + { + // see: https://github.com/xunit/xunit/issues/2146 + var formattedMessage = message.TrimEnd(Environment.NewLine.ToCharArray()); + this.output.WriteLine(formattedMessage); + this.logBuffer.Add(formattedMessage); + } } + +/// +/// A circular buffer that can be used to store a fixed number of items. +/// When the buffer is full, the oldest item is overwritten. +/// The buffer can be read in the same order as the items were added. +/// More information can be found here. +/// +/// +/// The buffer gets initialized by the call to the constructor and will allocate, +/// the memory for the buffer. The buffer is not resizable. +/// That means be carefull with , because it can cause an . +/// +/// The type of what the cicular buffer is off. +internal class CircularBuffer{ + private readonly int size; + private readonly T[] buffer; + private int readPosition = 0; + private int writePosition = 0; + /// + /// Initialize the buffer with the buffer size of . + /// + /// + /// The size the buffer will have + /// + public CircularBuffer(int size) + { + this.size = size; + buffer = new T[size]; + } + /// + /// Adds an item and move the write position to the next value + /// + /// The item that should be written. + public void Add(T item) + { + buffer[writePosition] = item; + writePosition = (writePosition + 1) % size; + } + /// + /// Reads on value and move the position to the next value + /// + /// + public T Read(){ + var value = buffer[readPosition]; + readPosition = (readPosition + 1) % size; + return value; + } + /// + /// Read the full buffer. + /// While the buffer is read, the read position is moved to the next value + /// + /// + public T[] ToArray() + { + var result = new T[size]; + for (int i = 0; i < size; i++) + { + result[i] = Read(); + } + return result; + } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/DaprRunConfiguration.cs b/test/Dapr.E2E.Test/DaprRunConfiguration.cs index fccfbcdd4..964cc6b2f 100644 --- a/test/Dapr.E2E.Test/DaprRunConfiguration.cs +++ b/test/Dapr.E2E.Test/DaprRunConfiguration.cs @@ -11,20 +11,19 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public class DaprRunConfiguration { - public class DaprRunConfiguration - { - public bool UseAppPort { get; set; } + public bool UseAppPort { get; set; } - public string AppId { get; set; } + public string AppId { get; set; } - public string AppProtocol { get; set; } + public string AppProtocol { get; set; } - public bool AppJsonSerialization { get; set; } + public bool AppJsonSerialization { get; set; } - public string ConfigurationPath { get; set; } + public string ConfigurationPath { get; set; } - public string TargetProject { get; set; } - } -} + public string TargetProject { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/DaprTestApp.cs b/test/Dapr.E2E.Test/DaprTestApp.cs index 8776a43d5..408dca3b8 100644 --- a/test/Dapr.E2E.Test/DaprTestApp.cs +++ b/test/Dapr.E2E.Test/DaprTestApp.cs @@ -22,147 +22,146 @@ using Xunit.Abstractions; using static System.IO.Path; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public class DaprTestApp { - public class DaprTestApp + static string daprBinaryName = "dapr"; + private string appId; + private readonly string[] outputToMatchOnStart = new string[] { "dapr initialized. Status: Running.", }; + private readonly string[] outputToMatchOnStop = new string[] { "app stopped successfully", "failed to stop app id", }; + + private ITestOutputHelper testOutput; + + public DaprTestApp(ITestOutputHelper output, string appId) { - static string daprBinaryName = "dapr"; - private string appId; - private readonly string[] outputToMatchOnStart = new string[] { "dapr initialized. Status: Running.", }; - private readonly string[] outputToMatchOnStop = new string[] { "app stopped successfully", "failed to stop app id", }; + this.appId = appId; + this.testOutput = output; + } + + public string AppId => this.appId; - private ITestOutputHelper testOutput; + public (string httpEndpoint, string grpcEndpoint) Start(DaprRunConfiguration configuration) + { + var (appPort, httpPort, grpcPort, metricsPort) = GetFreePorts(); - public DaprTestApp(ITestOutputHelper output, string appId) + var resourcesPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "components"); + var configPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "configuration", "featureconfig.yaml"); + var arguments = new List() { - this.appId = appId; - this.testOutput = output; + // `dapr run` args + "run", + "--app-id", configuration.AppId, + "--dapr-http-port", httpPort.ToString(CultureInfo.InvariantCulture), + "--dapr-grpc-port", grpcPort.ToString(CultureInfo.InvariantCulture), + "--metrics-port", metricsPort.ToString(CultureInfo.InvariantCulture), + "--resources-path", resourcesPath, + "--config", configPath, + "--log-level", "debug", + "--max-body-size", "8Mi" + }; + + if (configuration.UseAppPort) + { + arguments.AddRange(new[] { "--app-port", appPort.ToString(CultureInfo.InvariantCulture), }); } - public string AppId => this.appId; - - public (string httpEndpoint, string grpcEndpoint) Start(DaprRunConfiguration configuration) + if (!string.IsNullOrEmpty(configuration.AppProtocol)) { - var (appPort, httpPort, grpcPort, metricsPort) = GetFreePorts(); + arguments.AddRange(new[] { "--app-protocol", configuration.AppProtocol }); + } - var resourcesPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "components"); - var configPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "configuration", "featureconfig.yaml"); - var arguments = new List() - { - // `dapr run` args - "run", - "--app-id", configuration.AppId, - "--dapr-http-port", httpPort.ToString(CultureInfo.InvariantCulture), - "--dapr-grpc-port", grpcPort.ToString(CultureInfo.InvariantCulture), - "--metrics-port", metricsPort.ToString(CultureInfo.InvariantCulture), - "--resources-path", resourcesPath, - "--config", configPath, - "--log-level", "debug", - "--max-body-size", "8Mi" - }; - - if (configuration.UseAppPort) - { - arguments.AddRange(new[] { "--app-port", appPort.ToString(CultureInfo.InvariantCulture), }); - } + arguments.AddRange(new[] + { + // separator + "--", - if (!string.IsNullOrEmpty(configuration.AppProtocol)) - { - arguments.AddRange(new[] { "--app-protocol", configuration.AppProtocol }); - } + // `dotnet run` args + "dotnet", "run", + "--project", configuration.TargetProject, + "--framework", GetTargetFrameworkName(), + }); - arguments.AddRange(new[] - { - // separator - "--", + if (configuration.UseAppPort) + { + // The first argument is the port, if the application needs it. + arguments.AddRange(new[] { "--", $"{appPort.ToString(CultureInfo.InvariantCulture)}" }); + arguments.AddRange(new[] { "--urls", $"http://localhost:{appPort.ToString(CultureInfo.InvariantCulture)}", }); + } - // `dotnet run` args - "dotnet", "run", - "--project", configuration.TargetProject, - "--framework", GetTargetFrameworkName(), - }); + if (configuration.AppJsonSerialization) + { + arguments.AddRange(new[] { "--json-serialization" }); + } - if (configuration.UseAppPort) + // TODO: we don't do any quoting right now because our paths are guaranteed not to contain spaces + var daprStart = new DaprCommand(this.testOutput) + { + DaprBinaryName = DaprTestApp.daprBinaryName, + Command = string.Join(" ", arguments), + OutputToMatch = outputToMatchOnStart, + Timeout = TimeSpan.FromSeconds(30), + EnvironmentVariables = new Dictionary { - // The first argument is the port, if the application needs it. - arguments.AddRange(new[] { "--", $"{appPort.ToString(CultureInfo.InvariantCulture)}" }); - arguments.AddRange(new[] { "--urls", $"http://localhost:{appPort.ToString(CultureInfo.InvariantCulture)}", }); + { "APP_API_TOKEN", "abcdefg" } } + }; - if (configuration.AppJsonSerialization) - { - arguments.AddRange(new[] { "--json-serialization" }); - } + daprStart.Run(); - // TODO: we don't do any quoting right now because our paths are guaranteed not to contain spaces - var daprStart = new DaprCommand(this.testOutput) - { - DaprBinaryName = DaprTestApp.daprBinaryName, - Command = string.Join(" ", arguments), - OutputToMatch = outputToMatchOnStart, - Timeout = TimeSpan.FromSeconds(30), - EnvironmentVariables = new Dictionary - { - { "APP_API_TOKEN", "abcdefg" } - } - }; - - daprStart.Run(); - - testOutput.WriteLine($"Dapr app: {appId} started successfully"); - var httpEndpoint = $"http://localhost:{httpPort}"; - var grpcEndpoint = $"http://localhost:{grpcPort}"; - return (httpEndpoint, grpcEndpoint); - } + testOutput.WriteLine($"Dapr app: {appId} started successfully"); + var httpEndpoint = $"http://localhost:{httpPort}"; + var grpcEndpoint = $"http://localhost:{grpcPort}"; + return (httpEndpoint, grpcEndpoint); + } - public void Stop() + public void Stop() + { + var daprStopCommand = $" stop --app-id {appId}"; + var daprStop = new DaprCommand(this.testOutput) { - var daprStopCommand = $" stop --app-id {appId}"; - var daprStop = new DaprCommand(this.testOutput) - { - DaprBinaryName = DaprTestApp.daprBinaryName, - Command = daprStopCommand, - OutputToMatch = outputToMatchOnStop, - Timeout = TimeSpan.FromSeconds(30), - }; - daprStop.Run(); - testOutput.WriteLine($"Dapr app: {appId} stopped successfully"); - } + DaprBinaryName = DaprTestApp.daprBinaryName, + Command = daprStopCommand, + OutputToMatch = outputToMatchOnStop, + Timeout = TimeSpan.FromSeconds(30), + }; + daprStop.Run(); + testOutput.WriteLine($"Dapr app: {appId} stopped successfully"); + } + + private static string GetTargetFrameworkName() + { + var targetFrameworkName = ((TargetFrameworkAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(TargetFrameworkAttribute), false).FirstOrDefault()).FrameworkName; - private static string GetTargetFrameworkName() + return targetFrameworkName switch { - var targetFrameworkName = ((TargetFrameworkAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(TargetFrameworkAttribute), false).FirstOrDefault()).FrameworkName; + ".NETCoreApp,Version=v6.0" => "net6", + ".NETCoreApp,Version=v7.0" => "net7", + ".NETCoreApp,Version=v8.0" => "net8", + ".NETCoreApp,Version=v9.0" => "net9", + _ => throw new InvalidOperationException($"Unsupported target framework: {targetFrameworkName}") + }; + } - return targetFrameworkName switch - { - ".NETCoreApp,Version=v6.0" => "net6", - ".NETCoreApp,Version=v7.0" => "net7", - ".NETCoreApp,Version=v8.0" => "net8", - ".NETCoreApp,Version=v9.0" => "net9", - _ => throw new InvalidOperationException($"Unsupported target framework: {targetFrameworkName}") - }; + private static (int, int, int, int) GetFreePorts() + { + const int NUM_LISTENERS = 4; + IPAddress ip = IPAddress.Loopback; + var listeners = new TcpListener[NUM_LISTENERS]; + var ports = new int[NUM_LISTENERS]; + // Note: Starting only one listener at a time might end up returning the + // same port each time. + for (int i = 0; i < NUM_LISTENERS; i++) + { + listeners[i] = new TcpListener(ip, 0); + listeners[i].Start(); + ports[i] = ((IPEndPoint)listeners[i].LocalEndpoint).Port; } - private static (int, int, int, int) GetFreePorts() + for (int i = 0; i < NUM_LISTENERS; i++) { - const int NUM_LISTENERS = 4; - IPAddress ip = IPAddress.Loopback; - var listeners = new TcpListener[NUM_LISTENERS]; - var ports = new int[NUM_LISTENERS]; - // Note: Starting only one listener at a time might end up returning the - // same port each time. - for (int i = 0; i < NUM_LISTENERS; i++) - { - listeners[i] = new TcpListener(ip, 0); - listeners[i].Start(); - ports[i] = ((IPEndPoint)listeners[i].LocalEndpoint).Port; - } - - for (int i = 0; i < NUM_LISTENERS; i++) - { - listeners[i].Stop(); - } - return (ports[0], ports[1], ports[2], ports[3]); + listeners[i].Stop(); } + return (ports[0], ports[1], ports[2], ports[3]); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/DaprTestAppFixture.cs b/test/Dapr.E2E.Test/DaprTestAppFixture.cs index 4e7889457..6017d641a 100644 --- a/test/Dapr.E2E.Test/DaprTestAppFixture.cs +++ b/test/Dapr.E2E.Test/DaprTestAppFixture.cs @@ -14,103 +14,102 @@ using System.Threading.Tasks; using Xunit.Abstractions; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +// Guarantees we run the app a single time for the whole test class. +public class DaprTestAppFixture : IDisposable { - // Guarantees we run the app a single time for the whole test class. - public class DaprTestAppFixture : IDisposable - { - private readonly object @lock = new object(); - private Task task; + private readonly object @lock = new object(); + private Task task; - public void Dispose() + public void Dispose() + { + Task task; + lock (@lock) { - Task task; - lock (@lock) + if (this.task is null) { - if (this.task is null) - { - // App didn't start, do nothing. - return; - } - - task = this.task; - } - - State state; - try - { - state = this.task.GetAwaiter().GetResult(); - } - catch - { - // App failed during start, do nothing. + // App didn't start, do nothing. return; } + task = this.task; + } + + State state; + try + { + state = this.task.GetAwaiter().GetResult(); + } + catch + { + // App failed during start, do nothing. + return; + } + + try + { + state.App?.Stop(); + } + catch (Exception ex) + { try { - state.App?.Stop(); + // see: https://github.com/xunit/xunit/issues/2146 + state.Output.WriteLine("Failed to shut down app: " + ex); } - catch (Exception ex) + catch (InvalidOperationException) { - try - { - // see: https://github.com/xunit/xunit/issues/2146 - state.Output.WriteLine("Failed to shut down app: " + ex); - } - catch (InvalidOperationException) - { - } } } + } - public Task StartAsync(ITestOutputHelper output, DaprRunConfiguration configuration) + public Task StartAsync(ITestOutputHelper output, DaprRunConfiguration configuration) + { + lock (@lock) { - lock (@lock) + if (this.task is null) { - if (this.task is null) - { - this.task = Task.Run(() => Launch(output, configuration)); - } - - return this.task; + this.task = Task.Run(() => Launch(output, configuration)); } + + return this.task; } + } - private State Launch(ITestOutputHelper output, DaprRunConfiguration configuration) + private State Launch(ITestOutputHelper output, DaprRunConfiguration configuration) + { + var app = new DaprTestApp(output, configuration.AppId); + try + { + var (httpEndpoint, grpcEndpoint) = app.Start(configuration); + return new State() + { + App = app, + HttpEndpoint = httpEndpoint, + GrpcEndpoint = grpcEndpoint, + Output = output, + }; + } + catch (Exception startException) { - var app = new DaprTestApp(output, configuration.AppId); try { - var (httpEndpoint, grpcEndpoint) = app.Start(configuration); - return new State() - { - App = app, - HttpEndpoint = httpEndpoint, - GrpcEndpoint = grpcEndpoint, - Output = output, - }; + app.Stop(); + throw; } - catch (Exception startException) + catch (Exception stopException) { - try - { - app.Stop(); - throw; - } - catch (Exception stopException) - { - throw new AggregateException(startException, stopException); - } + throw new AggregateException(startException, stopException); } } + } - public class State - { - public string HttpEndpoint; - public string GrpcEndpoint; - public DaprTestApp App; - public ITestOutputHelper Output; - } + public class State + { + public string HttpEndpoint; + public string GrpcEndpoint; + public DaprTestApp App; + public ITestOutputHelper Output; } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/DaprTestAppLifecycle.cs b/test/Dapr.E2E.Test/DaprTestAppLifecycle.cs index 98103cda6..07b62b6a2 100644 --- a/test/Dapr.E2E.Test/DaprTestAppLifecycle.cs +++ b/test/Dapr.E2E.Test/DaprTestAppLifecycle.cs @@ -18,57 +18,56 @@ using Xunit; using Xunit.Abstractions; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public class DaprTestAppLifecycle : IClassFixture, IAsyncLifetime { - public class DaprTestAppLifecycle : IClassFixture, IAsyncLifetime + + private readonly ITestOutputHelper output; + private readonly DaprTestAppFixture fixture; + private DaprTestAppFixture.State state; + + public DaprTestAppLifecycle(ITestOutputHelper output, DaprTestAppFixture fixture) { + this.output = output; + this.fixture = fixture; + } - private readonly ITestOutputHelper output; - private readonly DaprTestAppFixture fixture; - private DaprTestAppFixture.State state; + public DaprRunConfiguration Configuration { get; set; } - public DaprTestAppLifecycle(ITestOutputHelper output, DaprTestAppFixture fixture) - { - this.output = output; - this.fixture = fixture; - } + public string AppId => this.state?.App.AppId; - public DaprRunConfiguration Configuration { get; set; } + public string HttpEndpoint => this.state?.HttpEndpoint; - public string AppId => this.state?.App.AppId; + public string GrpcEndpoint => this.state?.GrpcEndpoint; - public string HttpEndpoint => this.state?.HttpEndpoint; + public ITestOutputHelper Output => this.output; - public string GrpcEndpoint => this.state?.GrpcEndpoint; + public async Task InitializeAsync() + { + this.state = await this.fixture.StartAsync(this.output, this.Configuration); - public ITestOutputHelper Output => this.output; + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + using var client = new HttpClient(); - public async Task InitializeAsync() + while (!cts.IsCancellationRequested) { - this.state = await this.fixture.StartAsync(this.output, this.Configuration); - - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - using var client = new HttpClient(); + cts.Token.ThrowIfCancellationRequested(); - while (!cts.IsCancellationRequested) + var response = await client.GetAsync($"{HttpEndpoint}/v1.0/healthz"); + if (response.IsSuccessStatusCode) { - cts.Token.ThrowIfCancellationRequested(); - - var response = await client.GetAsync($"{HttpEndpoint}/v1.0/healthz"); - if (response.IsSuccessStatusCode) - { - return; - } - - await Task.Delay(TimeSpan.FromMilliseconds(250)); + return; } - throw new TimeoutException("Timed out waiting for daprd health check"); + await Task.Delay(TimeSpan.FromMilliseconds(250)); } - public Task DisposeAsync() - { - return Task.CompletedTask; - } + throw new TimeoutException("Timed out waiting for daprd health check"); + } + + public Task DisposeAsync() + { + return Task.CompletedTask; } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test/E2ETests.cs b/test/Dapr.E2E.Test/E2ETests.cs index bc469f715..45974a331 100644 --- a/test/Dapr.E2E.Test/E2ETests.cs +++ b/test/Dapr.E2E.Test/E2ETests.cs @@ -22,78 +22,77 @@ [assembly: CollectionBehavior(DisableTestParallelization = true)] -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +// We're using IClassFixture to manage the state we need across tests. +// +// So for that reason we need all of the E2E tests to be one big class. +// We're using partials to organize the functionality. +public partial class E2ETests : IClassFixture, IAsyncLifetime { - // We're using IClassFixture to manage the state we need across tests. - // - // So for that reason we need all of the E2E tests to be one big class. - // We're using partials to organize the functionality. - public partial class E2ETests : IClassFixture, IAsyncLifetime + private readonly Lazy proxyFactory; + private readonly DaprTestAppFixture fixture; + private DaprTestAppFixture.State state; + + public E2ETests(ITestOutputHelper output, DaprTestAppFixture fixture) { - private readonly Lazy proxyFactory; - private readonly DaprTestAppFixture fixture; - private DaprTestAppFixture.State state; + this.Output = output; + this.fixture = fixture; - public E2ETests(ITestOutputHelper output, DaprTestAppFixture fixture) + this.proxyFactory = new Lazy(() => { - this.Output = output; - this.fixture = fixture; + Debug.Assert(this.HttpEndpoint != null); + return new ActorProxyFactory(new ActorProxyOptions(){ HttpEndpoint = this.HttpEndpoint, }); + }); + } - this.proxyFactory = new Lazy(() => - { - Debug.Assert(this.HttpEndpoint != null); - return new ActorProxyFactory(new ActorProxyOptions(){ HttpEndpoint = this.HttpEndpoint, }); - }); - } + protected ITestOutputHelper Output { get; } - protected ITestOutputHelper Output { get; } + public DaprRunConfiguration Configuration { get; set; } = new DaprRunConfiguration + { + UseAppPort = true, + AppId = "testapp", + TargetProject = "./../../../../../test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj" + }; - public DaprRunConfiguration Configuration { get; set; } = new DaprRunConfiguration - { - UseAppPort = true, - AppId = "testapp", - TargetProject = "./../../../../../test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj" - }; + public string AppId => this.state?.App.AppId; - public string AppId => this.state?.App.AppId; + public string HttpEndpoint => this.state?.HttpEndpoint; - public string HttpEndpoint => this.state?.HttpEndpoint; + public string GrpcEndpoint => this.state?.GrpcEndpoint; - public string GrpcEndpoint => this.state?.GrpcEndpoint; + public IActorProxyFactory ProxyFactory => this.HttpEndpoint == null ? null : this.proxyFactory.Value; - public IActorProxyFactory ProxyFactory => this.HttpEndpoint == null ? null : this.proxyFactory.Value; + public async Task InitializeAsync() + { + this.state = await this.fixture.StartAsync(this.Output, this.Configuration); - public async Task InitializeAsync() - { - this.state = await this.fixture.StartAsync(this.Output, this.Configuration); + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + using var client = new HttpClient(); - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - using var client = new HttpClient(); + while (!cts.IsCancellationRequested) + { + cts.Token.ThrowIfCancellationRequested(); - while (!cts.IsCancellationRequested) + var response = await client.GetAsync($"{HttpEndpoint}/v1.0/healthz"); + if (response.IsSuccessStatusCode) { - cts.Token.ThrowIfCancellationRequested(); - - var response = await client.GetAsync($"{HttpEndpoint}/v1.0/healthz"); - if (response.IsSuccessStatusCode) - { - return; - } - - await Task.Delay(TimeSpan.FromMilliseconds(250)); + return; } - throw new TimeoutException("Timed out waiting for daprd health check"); + await Task.Delay(TimeSpan.FromMilliseconds(250)); } - public Task DisposeAsync() - { - return Task.CompletedTask; - } + throw new TimeoutException("Timed out waiting for daprd health check"); + } - protected async Task WaitForActorRuntimeAsync(IPingActor proxy, CancellationToken cancellationToken) - { - await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cancellationToken); - } + public Task DisposeAsync() + { + return Task.CompletedTask; + } + + protected async Task WaitForActorRuntimeAsync(IPingActor proxy, CancellationToken cancellationToken) + { + await ActorRuntimeChecker.WaitForActorRuntimeAsync(this.AppId, this.Output, proxy, cancellationToken); } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/HttpAssert.cs b/test/Dapr.E2E.Test/HttpAssert.cs index 5c3fd9595..6958fd818 100644 --- a/test/Dapr.E2E.Test/HttpAssert.cs +++ b/test/Dapr.E2E.Test/HttpAssert.cs @@ -16,40 +16,39 @@ using System.Threading.Tasks; using Xunit.Sdk; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +// Assertion methods for HTTP requests +public static class HttpAssert { - // Assertion methods for HTTP requests - public static class HttpAssert + public static async Task<(TValue value, string json)> AssertJsonResponseAsync(HttpResponseMessage response) { - public static async Task<(TValue value, string json)> AssertJsonResponseAsync(HttpResponseMessage response) + if (response.Content is null && !response.IsSuccessStatusCode) + { + var message = $"The response had status code {response.StatusCode} and no body."; + throw new XunitException(message); + } + else if (!response.IsSuccessStatusCode) + { + var text = await response.Content.ReadAsStringAsync(); + var message = $"The response had status code {response.StatusCode} and body: " + Environment.NewLine + text; + throw new XunitException(message); + } + else { - if (response.Content is null && !response.IsSuccessStatusCode) + // Use the string to read from JSON so we can include it in the error if it fails. + var text = await response.Content.ReadAsStringAsync(); + + try { - var message = $"The response had status code {response.StatusCode} and no body."; - throw new XunitException(message); + var value = JsonSerializer.Deserialize(text, new JsonSerializerOptions(JsonSerializerDefaults.Web)); + return (value, text); } - else if (!response.IsSuccessStatusCode) + catch (JsonException ex) { - var text = await response.Content.ReadAsStringAsync(); - var message = $"The response had status code {response.StatusCode} and body: " + Environment.NewLine + text; + var message = $"The response failed to deserialize to a {typeof(TValue).Name} from JSON with error: '{ex.Message}' and body: " + Environment.NewLine + text; throw new XunitException(message); } - else - { - // Use the string to read from JSON so we can include it in the error if it fails. - var text = await response.Content.ReadAsStringAsync(); - - try - { - var value = JsonSerializer.Deserialize(text, new JsonSerializerOptions(JsonSerializerDefaults.Web)); - return (value, text); - } - catch (JsonException ex) - { - var message = $"The response failed to deserialize to a {typeof(TValue).Name} from JSON with error: '{ex.Message}' and body: " + Environment.NewLine + text; - throw new XunitException(message); - } - } } } -} +} \ No newline at end of file diff --git a/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.GrpcProxyInvocationTests.cs b/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.GrpcProxyInvocationTests.cs index 3b6f31e84..de77f7395 100644 --- a/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.GrpcProxyInvocationTests.cs +++ b/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.GrpcProxyInvocationTests.cs @@ -20,79 +20,78 @@ using Xunit; using Xunit.Abstractions; -namespace Dapr.E2E.Test +namespace Dapr.E2E.Test; + +public class GrpcProxyTests : DaprTestAppLifecycle { - public class GrpcProxyTests : DaprTestAppLifecycle + // Grpc proxying requires a specific setup so we can't use the built-in standard for the E2ETest partial class. + public GrpcProxyTests(ITestOutputHelper output, DaprTestAppFixture fixture) : base(output, fixture) { - // Grpc proxying requires a specific setup so we can't use the built-in standard for the E2ETest partial class. - public GrpcProxyTests(ITestOutputHelper output, DaprTestAppFixture fixture) : base(output, fixture) - { - AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - base.Configuration = new DaprRunConfiguration - { - UseAppPort = true, - AppId = "grpcapp", - AppProtocol = "grpc", - TargetProject = "./../../../../../test/Dapr.E2E.Test.App.Grpc/Dapr.E2E.Test.App.Grpc.csproj", - }; - } - - [Fact] - public async Task TestGrpcProxyMessageSendAndReceive() + AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); + base.Configuration = new DaprRunConfiguration { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - var invoker = DaprClient.CreateInvocationInvoker(appId: this.AppId, daprEndpoint: this.GrpcEndpoint); - var client = new Messager.MessagerClient(invoker); + UseAppPort = true, + AppId = "grpcapp", + AppProtocol = "grpc", + TargetProject = "./../../../../../test/Dapr.E2E.Test.App.Grpc/Dapr.E2E.Test.App.Grpc.csproj", + }; + } - await client.SendMessageAsync(new SendMessageRequest { Recipient = "Client", Message = "Hello"}, cancellationToken: cts.Token); + [Fact] + public async Task TestGrpcProxyMessageSendAndReceive() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); + var invoker = DaprClient.CreateInvocationInvoker(appId: this.AppId, daprEndpoint: this.GrpcEndpoint); + var client = new Messager.MessagerClient(invoker); - var response = await client.GetMessageAsync(new GetMessageRequest { Recipient = "Client" }, cancellationToken: cts.Token); + await client.SendMessageAsync(new SendMessageRequest { Recipient = "Client", Message = "Hello"}, cancellationToken: cts.Token); - Assert.Equal("Hello", response.Message); - } + var response = await client.GetMessageAsync(new GetMessageRequest { Recipient = "Client" }, cancellationToken: cts.Token); - [Fact] - public async Task TestGrpcProxyStreamingBroadcast() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - var invoker = DaprClient.CreateInvocationInvoker(appId: this.AppId, daprEndpoint: this.GrpcEndpoint); - var client = new Messager.MessagerClient(invoker); + Assert.Equal("Hello", response.Message); + } - var messageCount = 10; - var messageReceived = 0; - using (var call = client.StreamBroadcast(cancellationToken: cts.Token)) { - var responseTask = Task.Run(async () => - { - await foreach (var response in call.ResponseStream.ReadAllAsync()) - { - Assert.Equal($"Hello: {messageReceived++}", response.Message); - } - }); + [Fact] + public async Task TestGrpcProxyStreamingBroadcast() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); + var invoker = DaprClient.CreateInvocationInvoker(appId: this.AppId, daprEndpoint: this.GrpcEndpoint); + var client = new Messager.MessagerClient(invoker); - for (var i = 0; i < messageCount; i++) + var messageCount = 10; + var messageReceived = 0; + using (var call = client.StreamBroadcast(cancellationToken: cts.Token)) { + var responseTask = Task.Run(async () => + { + await foreach (var response in call.ResponseStream.ReadAllAsync()) { - await call.RequestStream.WriteAsync(new Broadcast { Message = $"Hello: {i}" }); + Assert.Equal($"Hello: {messageReceived++}", response.Message); } - await call.RequestStream.CompleteAsync(); + }); - await responseTask; + for (var i = 0; i < messageCount; i++) + { + await call.RequestStream.WriteAsync(new Broadcast { Message = $"Hello: {i}" }); } + await call.RequestStream.CompleteAsync(); + + await responseTask; } + } - [Fact] - public async Task TestGrpcServiceInvocationWithTimeout() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - var invoker = DaprClient.CreateInvocationInvoker(appId: this.AppId, daprEndpoint: this.GrpcEndpoint); - var client = new Messager.MessagerClient(invoker); + [Fact] + public async Task TestGrpcServiceInvocationWithTimeout() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); + var invoker = DaprClient.CreateInvocationInvoker(appId: this.AppId, daprEndpoint: this.GrpcEndpoint); + var client = new Messager.MessagerClient(invoker); - var options = new CallOptions(cancellationToken: cts.Token, deadline: DateTime.UtcNow.AddSeconds(1)); - var ex = await Assert.ThrowsAsync(async () => - { - await client.DelayedResponseAsync(new Empty(), options); - }); + var options = new CallOptions(cancellationToken: cts.Token, deadline: DateTime.UtcNow.AddSeconds(1)); + var ex = await Assert.ThrowsAsync(async () => + { + await client.DelayedResponseAsync(new Empty(), options); + }); - Assert.Equal(StatusCode.DeadlineExceeded, ex.StatusCode); - } + Assert.Equal(StatusCode.DeadlineExceeded, ex.StatusCode); } } \ No newline at end of file diff --git a/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs b/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs index 78fed3d30..af94981cb 100644 --- a/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs +++ b/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs @@ -10,85 +10,84 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.E2E.Test -{ - using System; - using System.Net.Http; - using System.Net.Http.Json; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Client; - using Xunit; - - public partial class E2ETests - { - [Fact] - public async Task TestServiceInvocation() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - - using var client = DaprClient.CreateInvokeHttpClient(appId: this.AppId, daprEndpoint: this.HttpEndpoint); - var transaction = new Transaction() - { - Id = "1", - Amount = 50 - }; +namespace Dapr.E2E.Test; - var response = await client.PostAsJsonAsync("/accountDetails", transaction, cts.Token); - var (account, _) = await HttpAssert.AssertJsonResponseAsync(response); +using System; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Client; +using Xunit; - Assert.Equal("1", account.Id); - Assert.Equal(150, account.Balance); - } +public partial class E2ETests +{ + [Fact] + public async Task TestServiceInvocation() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - [Fact] - public async Task TestServiceInvocationRequiresApiToken() + using var client = DaprClient.CreateInvokeHttpClient(appId: this.AppId, daprEndpoint: this.HttpEndpoint); + var transaction = new Transaction() { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); + Id = "1", + Amount = 50 + }; - using var client = DaprClient.CreateInvokeHttpClient(appId: this.AppId, daprEndpoint: this.HttpEndpoint); + var response = await client.PostAsJsonAsync("/accountDetails", transaction, cts.Token); + var (account, _) = await HttpAssert.AssertJsonResponseAsync(response); + + Assert.Equal("1", account.Id); + Assert.Equal(150, account.Balance); + } - var transaction = new Transaction() - { - Id = "1", - Amount = 50 - }; - var response = await client.PostAsJsonAsync("/accountDetails-requires-api-token", transaction, cts.Token); - var (account, _) = await HttpAssert.AssertJsonResponseAsync(response); + [Fact] + public async Task TestServiceInvocationRequiresApiToken() + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - Assert.Equal("1", account.Id); - Assert.Equal(150, account.Balance); - } + using var client = DaprClient.CreateInvokeHttpClient(appId: this.AppId, daprEndpoint: this.HttpEndpoint); - [Fact] - public async Task TestHttpServiceInvocationWithTimeout() + var transaction = new Transaction() { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - using var client = new DaprClientBuilder() - .UseHttpEndpoint(this.HttpEndpoint) - .UseTimeout(TimeSpan.FromSeconds(1)) - .Build(); + Id = "1", + Amount = 50 + }; + var response = await client.PostAsJsonAsync("/accountDetails-requires-api-token", transaction, cts.Token); + var (account, _) = await HttpAssert.AssertJsonResponseAsync(response); - await Assert.ThrowsAsync(async () => - { - await client.InvokeMethodAsync( - appId: this.AppId, - methodName: "DelayedResponse", - httpMethod: new HttpMethod("GET"), - cancellationToken: cts.Token); - }); - } + Assert.Equal("1", account.Id); + Assert.Equal(150, account.Balance); } - internal class Transaction + [Fact] + public async Task TestHttpServiceInvocationWithTimeout() { - public string Id { get; set; } - public decimal Amount { get; set; } - } + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); + using var client = new DaprClientBuilder() + .UseHttpEndpoint(this.HttpEndpoint) + .UseTimeout(TimeSpan.FromSeconds(1)) + .Build(); - internal class Account - { - public string Id { get; set; } - public decimal Balance { get; set; } + await Assert.ThrowsAsync(async () => + { + await client.InvokeMethodAsync( + appId: this.AppId, + methodName: "DelayedResponse", + httpMethod: new HttpMethod("GET"), + cancellationToken: cts.Token); + }); } } + +internal class Transaction +{ + public string Id { get; set; } + public decimal Amount { get; set; } +} + +internal class Account +{ + public string Id { get; set; } + public decimal Balance { get; set; } +} \ No newline at end of file diff --git a/test/Dapr.Extensions.Configuration.Test/DaprConfigurationStoreProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprConfigurationStoreProviderTest.cs index ee86a2067..a1cb299f7 100644 --- a/test/Dapr.Extensions.Configuration.Test/DaprConfigurationStoreProviderTest.cs +++ b/test/Dapr.Extensions.Configuration.Test/DaprConfigurationStoreProviderTest.cs @@ -8,201 +8,200 @@ using Xunit; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; -namespace Dapr.Extensions.Configuration.Test +namespace Dapr.Extensions.Configuration.Test; + +public class DaprConfigurationStoreProviderTest { - public class DaprConfigurationStoreProviderTest + [Fact] + public void TestConfigurationStoreExtension_ThrowsWithNullStore() { - [Fact] - public void TestConfigurationStoreExtension_ThrowsWithNullStore() - { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) - .Build(); - - Assert.Throws(() => - { - new ConfigurationBuilder().AddDaprConfigurationStore(null, new List(), daprClient, TimeSpan.FromSeconds(5)); - }); - } + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void TestConfigurationStoreExtension_ThrowsWithEmptyStore() + Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) - .Build(); + new ConfigurationBuilder().AddDaprConfigurationStore(null, new List(), daprClient, TimeSpan.FromSeconds(5)); + }); + } - Assert.Throws(() => - { - new ConfigurationBuilder().AddDaprConfigurationStore(string.Empty, new List(), daprClient, TimeSpan.FromSeconds(5)); - }); - } + [Fact] + public void TestConfigurationStoreExtension_ThrowsWithEmptyStore() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void TestConfigurationStoreExtension_ThrowsWithNullKeys() + Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) - .Build(); + new ConfigurationBuilder().AddDaprConfigurationStore(string.Empty, new List(), daprClient, TimeSpan.FromSeconds(5)); + }); + } - Assert.Throws(() => - { - new ConfigurationBuilder().AddDaprConfigurationStore("configstore", null, daprClient, TimeSpan.FromSeconds(5)); - }); - } + [Fact] + public void TestConfigurationStoreExtension_ThrowsWithNullKeys() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void TestConfigurationStoreExtension_ThrowsWithNullClient() + Assert.Throws(() => { - Assert.Throws(() => - { - new ConfigurationBuilder().AddDaprConfigurationStore("configstore", new List(), null, TimeSpan.FromSeconds(5)); - }); - } + new ConfigurationBuilder().AddDaprConfigurationStore("configstore", null, daprClient, TimeSpan.FromSeconds(5)); + }); + } - [Fact] - public void TestSubscribeConfigurationStoreExtension_ThrowsWithNullStore() + [Fact] + public void TestConfigurationStoreExtension_ThrowsWithNullClient() + { + Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) - .Build(); + new ConfigurationBuilder().AddDaprConfigurationStore("configstore", new List(), null, TimeSpan.FromSeconds(5)); + }); + } - Assert.Throws(() => - { - new ConfigurationBuilder().AddStreamingDaprConfigurationStore(null, new List(), daprClient, TimeSpan.FromSeconds(5)); - }); - } + [Fact] + public void TestSubscribeConfigurationStoreExtension_ThrowsWithNullStore() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void TestSubscribeConfigurationStoreExtension_ThrowsWithEmptyStore() + Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) - .Build(); + new ConfigurationBuilder().AddStreamingDaprConfigurationStore(null, new List(), daprClient, TimeSpan.FromSeconds(5)); + }); + } - Assert.Throws(() => - { - new ConfigurationBuilder().AddStreamingDaprConfigurationStore(string.Empty, new List(), daprClient, TimeSpan.FromSeconds(5)); - }); - } + [Fact] + public void TestSubscribeConfigurationStoreExtension_ThrowsWithEmptyStore() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void TestSubscribeConfigurationStoreExtension_ThrowsWithNullKeys() + Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) - .Build(); + new ConfigurationBuilder().AddStreamingDaprConfigurationStore(string.Empty, new List(), daprClient, TimeSpan.FromSeconds(5)); + }); + } - Assert.Throws(() => - { - new ConfigurationBuilder().AddStreamingDaprConfigurationStore("configstore", null, daprClient, TimeSpan.FromSeconds(5)); - }); - } + [Fact] + public void TestSubscribeConfigurationStoreExtension_ThrowsWithNullKeys() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void TestSubscribeConfigurationStoreExtension_ThrowsWithNullClient() + Assert.Throws(() => { - Assert.Throws(() => - { - new ConfigurationBuilder().AddStreamingDaprConfigurationStore("configstore", new List(), null, TimeSpan.FromSeconds(5)); - }); - } + new ConfigurationBuilder().AddStreamingDaprConfigurationStore("configstore", null, daprClient, TimeSpan.FromSeconds(5)); + }); + } - [Fact] - public async Task TestConfigurationStoreExtension_ProperlyStoresValues() + [Fact] + public void TestSubscribeConfigurationStoreExtension_ThrowsWithNullClient() + { + Assert.Throws(() => { - // Sample item. - var item = new ConfigurationItem("testValue", "v1", null); + new ConfigurationBuilder().AddStreamingDaprConfigurationStore("configstore", new List(), null, TimeSpan.FromSeconds(5)); + }); + } - // Configure Client - var httpClient = new TestHttpClient() + [Fact] + public async Task TestConfigurationStoreExtension_ProperlyStoresValues() + { + // Sample item. + var item = new ConfigurationItem("testValue", "v1", null); + + // Configure Client + var httpClient = new TestHttpClient() + { + Handler = async (entry) => { - Handler = async (entry) => - { - var items = new Dictionary(); - items["testKey"] = item; - await SendResponseWithConfiguration(items, entry); - } - }; + var items = new Dictionary(); + items["testKey"] = item; + await SendResponseWithConfiguration(items, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = new ConfigurationBuilder() - .AddDaprConfigurationStore("store", new List(), daprClient, TimeSpan.FromSeconds(5)) - .Build(); + var config = new ConfigurationBuilder() + .AddDaprConfigurationStore("store", new List(), daprClient, TimeSpan.FromSeconds(5)) + .Build(); - await Task.Delay(TimeSpan.FromMilliseconds(500)); + await Task.Delay(TimeSpan.FromMilliseconds(500)); - Assert.Equal(item.Value, config["testKey"]); - } + Assert.Equal(item.Value, config["testKey"]); + } - [Fact] - public async Task TestStreamingConfigurationStoreExtension_ProperlyStoresValues() - { - // Sample item. - var item = new ConfigurationItem("testValue", "v1", null); + [Fact] + public async Task TestStreamingConfigurationStoreExtension_ProperlyStoresValues() + { + // Sample item. + var item = new ConfigurationItem("testValue", "v1", null); - // Configure Client - var httpClient = new TestHttpClient() + // Configure Client + var httpClient = new TestHttpClient() + { + Handler = async (entry) => { - Handler = async (entry) => - { - var items = new Dictionary(); - items["testKey"] = item; - await SendStreamingResponseWithConfiguration(items, entry); - } - }; + var items = new Dictionary(); + items["testKey"] = item; + await SendStreamingResponseWithConfiguration(items, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = new ConfigurationBuilder() - .AddStreamingDaprConfigurationStore("store", new List(), daprClient, TimeSpan.FromSeconds(5)) - .Build(); + var config = new ConfigurationBuilder() + .AddStreamingDaprConfigurationStore("store", new List(), daprClient, TimeSpan.FromSeconds(5)) + .Build(); - await Task.Delay(TimeSpan.FromMilliseconds(500)); + await Task.Delay(TimeSpan.FromMilliseconds(500)); - Assert.Equal(item.Value, config["testKey"]); - } + Assert.Equal(item.Value, config["testKey"]); + } - private async Task SendResponseWithConfiguration(Dictionary items, TestHttpClient.Entry entry) + private async Task SendResponseWithConfiguration(Dictionary items, TestHttpClient.Entry entry) + { + var configurationResponse = new Autogenerated.GetConfigurationResponse(); + foreach (var item in items) { - var configurationResponse = new Autogenerated.GetConfigurationResponse(); - foreach (var item in items) + configurationResponse.Items[item.Key] = new Autogenerated.ConfigurationItem() { - configurationResponse.Items[item.Key] = new Autogenerated.ConfigurationItem() - { - Value = item.Value.Value, - Version = item.Value.Version - }; - } - - var streamContent = await GrpcUtils.CreateResponseContent(configurationResponse); - var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); - entry.Completion.SetResult(response); + Value = item.Value.Value, + Version = item.Value.Version + }; } - private async Task SendStreamingResponseWithConfiguration(Dictionary items, TestHttpClient.Entry entry) + var streamContent = await GrpcUtils.CreateResponseContent(configurationResponse); + var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); + entry.Completion.SetResult(response); + } + + private async Task SendStreamingResponseWithConfiguration(Dictionary items, TestHttpClient.Entry entry) + { + var streamResponse = new Autogenerated.SubscribeConfigurationResponse(); + streamResponse.Id = "testId"; + foreach (var item in items) { - var streamResponse = new Autogenerated.SubscribeConfigurationResponse(); - streamResponse.Id = "testId"; - foreach (var item in items) + streamResponse.Items[item.Key] = new Autogenerated.ConfigurationItem() { - streamResponse.Items[item.Key] = new Autogenerated.ConfigurationItem() - { - Value = item.Value.Value, - Version = item.Value.Version - }; - } - - var streamContent = await GrpcUtils.CreateResponseContent(streamResponse); - var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); - entry.Completion.SetResult(response); + Value = item.Value.Value, + Version = item.Value.Version + }; } + + var streamContent = await GrpcUtils.CreateResponseContent(streamResponse); + var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); + entry.Completion.SetResult(response); } -} +} \ No newline at end of file diff --git a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs index d92fc46b4..53cb4fc6c 100644 --- a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs +++ b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs @@ -23,916 +23,915 @@ using Xunit; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; -namespace Dapr.Extensions.Configuration.Test +namespace Dapr.Extensions.Configuration.Test; + +// These tests use the outdated TestHttpClient infrastructure because they need to +// support testing with synchronous HTTP requests. +// +// Don't copy this pattern elsewhere. +public class DaprSecretStoreConfigurationProviderTest { - // These tests use the outdated TestHttpClient infrastructure because they need to - // support testing with synchronous HTTP requests. - // - // Don't copy this pattern elsewhere. - public class DaprSecretStoreConfigurationProviderTest + + [Fact] + public void AddDaprSecretStore_UsingDescriptors_WithoutStore_ReportsError() { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void AddDaprSecretStore_UsingDescriptors_WithoutStore_ReportsError() + var ex = Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + CreateBuilder() + .AddDaprSecretStore(null, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore(null, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + Assert.Contains("store", ex.Message); + } - Assert.Contains("store", ex.Message); - } + [Fact] + public void AddDaprSecretStore_UsingDescriptors_WithEmptyStore_ReportsError() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void AddDaprSecretStore_UsingDescriptors_WithEmptyStore_ReportsError() + var ex = Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + CreateBuilder() + .AddDaprSecretStore(string.Empty, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore(string.Empty, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + Assert.Contains("The value cannot be null or empty", ex.Message); + } - Assert.Contains("The value cannot be null or empty", ex.Message); - } + [Fact] + public void AddDaprSecretStore_UsingDescriptors_WithoutSecretDescriptors_ReportsError() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void AddDaprSecretStore_UsingDescriptors_WithoutSecretDescriptors_ReportsError() + var ex = Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + CreateBuilder() + .AddDaprSecretStore("store", (DaprSecretDescriptor[])null, daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore("store", (DaprSecretDescriptor[])null, daprClient) - .Build(); - }); - - Assert.Contains("secretDescriptors", ex.Message); - } + Assert.Contains("secretDescriptors", ex.Message); + } - [Fact] - public void AddDaprSecretStore_UsingDescriptors_WithoutClient_ReportsError() + [Fact] + public void AddDaprSecretStore_UsingDescriptors_WithoutClient_ReportsError() + { + var ex = Assert.Throws(() => { - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, null) - .Build(); - }); + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, null) + .Build(); + }); - Assert.Contains("client", ex.Message); - } + Assert.Contains("client", ex.Message); + } + + [Fact] + public void AddDaprSecretStore_UsingDescriptors_WithZeroSecretDescriptors_ReportsError() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void AddDaprSecretStore_UsingDescriptors_WithZeroSecretDescriptors_ReportsError() + var ex = Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { }, daprClient) .Build(); + }); + + Assert.Contains("No secret descriptor was provided", ex.Message); + } - var ex = Assert.Throws(() => + [Fact] + public void AddDaprSecretStore_UsingDescriptors_DuplicateSecret_ReportsError() + { + var httpClient = new TestHttpClient() + { + Handler = async (entry) => { - CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { }, daprClient) - .Build(); - }); + var secrets = new Dictionary() { { "secretName", "secret" }, { "SecretName", "secret" } }; + await SendResponseWithSecrets(secrets, entry); + } + }; - Assert.Contains("No secret descriptor was provided", ex.Message); - } + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - [Fact] - public void AddDaprSecretStore_UsingDescriptors_DuplicateSecret_ReportsError() + var ex = Assert.Throws(() => { - var httpClient = new TestHttpClient() - { - Handler = async (entry) => - { - var secrets = new Dictionary() { { "secretName", "secret" }, { "SecretName", "secret" } }; - await SendResponseWithSecrets(secrets, entry); - } - }; - - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); + } - Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); - } + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsOneValue() + { + var storeName = "store"; + var secretKey = "secretName"; + var secretValue = "secret"; - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsOneValue() + var secretDescriptors = new[] { - var storeName = "store"; - var secretKey = "secretName"; - var secretValue = "secret"; + new DaprSecretDescriptor(secretKey), + }; - var secretDescriptors = new[] - { - new DaprSecretDescriptor(secretKey), - }; + var daprClient = new Mock(); - var daprClient = new Mock(); + daprClient.Setup(c => c.GetSecretAsync(storeName, secretKey, + It.IsAny>(), default)) + .ReturnsAsync(new Dictionary { { secretKey, secretValue } }); - daprClient.Setup(c => c.GetSecretAsync(storeName, secretKey, - It.IsAny>(), default)) - .ReturnsAsync(new Dictionary { { secretKey, secretValue } }); + var config = CreateBuilder() + .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) - .Build(); + config["secretName"].ShouldBe("secret"); + } - config["secretName"].ShouldBe("secret"); - } + [Fact] + public void LoadSecrets_FromSecretStoreThatCanReturnsMultipleValues() + { + var storeName = "store"; + var firstSecretKey = "first_secret"; + var secondSecretKey = "second_secret"; + var firstSecretValue = "secret1"; + var secondSecretValue = "secret2"; - [Fact] - public void LoadSecrets_FromSecretStoreThatCanReturnsMultipleValues() + var secretDescriptors = new[] { - var storeName = "store"; - var firstSecretKey = "first_secret"; - var secondSecretKey = "second_secret"; - var firstSecretValue = "secret1"; - var secondSecretValue = "secret2"; + new DaprSecretDescriptor(firstSecretKey), + new DaprSecretDescriptor(secondSecretKey), + }; - var secretDescriptors = new[] - { - new DaprSecretDescriptor(firstSecretKey), - new DaprSecretDescriptor(secondSecretKey), - }; + var daprClient = new Mock(); - var daprClient = new Mock(); + daprClient.Setup(c => c.GetSecretAsync(storeName, firstSecretKey, + It.IsAny>(), default)) + .ReturnsAsync(new Dictionary { { firstSecretKey, firstSecretValue } }); - daprClient.Setup(c => c.GetSecretAsync(storeName, firstSecretKey, - It.IsAny>(), default)) - .ReturnsAsync(new Dictionary { { firstSecretKey, firstSecretValue } }); + daprClient.Setup(c => c.GetSecretAsync(storeName, secondSecretKey, + It.IsAny>(), default)) + .ReturnsAsync(new Dictionary { { secondSecretKey, secondSecretValue } }); - daprClient.Setup(c => c.GetSecretAsync(storeName, secondSecretKey, - It.IsAny>(), default)) - .ReturnsAsync(new Dictionary { { secondSecretKey, secondSecretValue } }); + var config = CreateBuilder() + .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) - .Build(); + config[firstSecretKey].ShouldBe(firstSecretValue); + config[secondSecretKey].ShouldBe(secondSecretValue); + } - config[firstSecretKey].ShouldBe(firstSecretValue); - config[secondSecretKey].ShouldBe(secondSecretValue); - } + [Fact] + public void LoadSecrets_FromSecretStoreThatCanReturnsMultivaluedValues() + { + var storeName = "store"; + var parentSecretKey = "connectionStrings"; + var firstSecretKey = "first_secret"; + var secondSecretKey = "second_secret"; + var firstSecretValue = "secret1"; + var secondSecretValue = "secret2"; - [Fact] - public void LoadSecrets_FromSecretStoreThatCanReturnsMultivaluedValues() + var secretDescriptors = new[] { - var storeName = "store"; - var parentSecretKey = "connectionStrings"; - var firstSecretKey = "first_secret"; - var secondSecretKey = "second_secret"; - var firstSecretValue = "secret1"; - var secondSecretValue = "secret2"; + new DaprSecretDescriptor(parentSecretKey) + }; - var secretDescriptors = new[] - { - new DaprSecretDescriptor(parentSecretKey) - }; + var daprClient = new Mock(); - var daprClient = new Mock(); + daprClient.Setup(c => c.GetSecretAsync(storeName, parentSecretKey, + It.IsAny>(), default)) + .ReturnsAsync(new Dictionary { { firstSecretKey, firstSecretValue }, { secondSecretKey, secondSecretValue } }); - daprClient.Setup(c => c.GetSecretAsync(storeName, parentSecretKey, - It.IsAny>(), default)) - .ReturnsAsync(new Dictionary { { firstSecretKey, firstSecretValue }, { secondSecretKey, secondSecretValue } }); + var config = CreateBuilder() + .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) - .Build(); + config[firstSecretKey].ShouldBe(firstSecretValue); + config[secondSecretKey].ShouldBe(secondSecretValue); + } - config[firstSecretKey].ShouldBe(firstSecretValue); - config[secondSecretKey].ShouldBe(secondSecretValue); - } + [Fact] + public void LoadSecrets_FromSecretStoreWithADifferentSecretKeyAndName() + { + var storeName = "store"; + var secretKey = "Microsservice-DatabaseConnStr"; + var secretName = "ConnectionStrings:DatabaseConnStr"; + var secretValue = "secret1"; - [Fact] - public void LoadSecrets_FromSecretStoreWithADifferentSecretKeyAndName() + var secretDescriptors = new[] { - var storeName = "store"; - var secretKey = "Microsservice-DatabaseConnStr"; - var secretName = "ConnectionStrings:DatabaseConnStr"; - var secretValue = "secret1"; + new DaprSecretDescriptor(secretName, new Dictionary(), true, + secretKey) + }; - var secretDescriptors = new[] - { - new DaprSecretDescriptor(secretName, new Dictionary(), true, - secretKey) - }; + var daprClient = new Mock(); - var daprClient = new Mock(); + daprClient.Setup(c => c.GetSecretAsync(storeName, secretKey, + It.IsAny>(), default)) + .ReturnsAsync(new Dictionary { { secretKey, secretValue } }); - daprClient.Setup(c => c.GetSecretAsync(storeName, secretKey, - It.IsAny>(), default)) - .ReturnsAsync(new Dictionary { { secretKey, secretValue } }); + var config = CreateBuilder() + .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) - .Build(); + config[secretName].ShouldBe(secretValue); + } - config[secretName].ShouldBe(secretValue); - } + [Fact] + public void LoadSecrets_FromSecretStoreNotRequiredAndDoesNotExist_ShouldNotThrowException() + { + var storeName = "store"; + var secretName = "ConnectionStrings:DatabaseConnStr"; - [Fact] - public void LoadSecrets_FromSecretStoreNotRequiredAndDoesNotExist_ShouldNotThrowException() + var secretDescriptors = new[] { - var storeName = "store"; - var secretName = "ConnectionStrings:DatabaseConnStr"; + new DaprSecretDescriptor(secretName, new Dictionary(), false) + }; - var secretDescriptors = new[] + var httpClient = new TestHttpClient + { + Handler = async entry => { - new DaprSecretDescriptor(secretName, new Dictionary(), false) - }; + await SendEmptyResponse(entry); + } + }; - var httpClient = new TestHttpClient - { - Handler = async entry => - { - await SendEmptyResponse(entry); - } - }; + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .Build(); - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .Build(); + var config = CreateBuilder() + .AddDaprSecretStore(storeName, secretDescriptors, daprClient) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore(storeName, secretDescriptors, daprClient) - .Build(); + config[secretName].ShouldBeNull(); + } - config[secretName].ShouldBeNull(); - } + [Fact] + public void LoadSecrets_FromSecretStoreRequiredAndDoesNotExist_ShouldThrowException() + { + var storeName = "store"; + var secretName = "ConnectionStrings:DatabaseConnStr"; - [Fact] - public void LoadSecrets_FromSecretStoreRequiredAndDoesNotExist_ShouldThrowException() + var secretDescriptors = new[] { - var storeName = "store"; - var secretName = "ConnectionStrings:DatabaseConnStr"; + new DaprSecretDescriptor(secretName, new Dictionary(), true) + }; - var secretDescriptors = new[] + var httpClient = new TestHttpClient + { + Handler = async entry => { - new DaprSecretDescriptor(secretName, new Dictionary(), true) - }; + await SendEmptyResponse(entry); + } + }; - var httpClient = new TestHttpClient - { - Handler = async entry => - { - await SendEmptyResponse(entry); - } - }; + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .Build(); - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) + var ex = Assert.Throws(() => + { + CreateBuilder() + .AddDaprSecretStore(storeName, secretDescriptors, daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore(storeName, secretDescriptors, daprClient) - .Build(); - }); + Assert.Contains("Secret", ex.Message); + } - Assert.Contains("Secret", ex.Message); - } + [Fact] + public void AddDaprSecretStore_WithoutStore_ReportsError() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void AddDaprSecretStore_WithoutStore_ReportsError() + var ex = Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + CreateBuilder() + .AddDaprSecretStore(null, daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore(null, daprClient) - .Build(); - }); + Assert.Contains("store", ex.Message); + } - Assert.Contains("store", ex.Message); - } + [Fact] + public void AddDaprSecretStore_WithEmptyStore_ReportsError() + { + var daprClient = new DaprClientBuilder() + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + .Build(); - [Fact] - public void AddDaprSecretStore_WithEmptyStore_ReportsError() + var ex = Assert.Throws(() => { - var daprClient = new DaprClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) + CreateBuilder() + .AddDaprSecretStore(string.Empty, daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore(string.Empty, daprClient) - .Build(); - }); + Assert.Contains("The value cannot be null or empty", ex.Message); + } - Assert.Contains("The value cannot be null or empty", ex.Message); - } + [Fact] + public void AddDaprSecretStore_WithoutClient_ReportsError() + { + var ex = Assert.Throws(() => + { + CreateBuilder() + .AddDaprSecretStore("store", null) + .Build(); + }); + + Assert.Contains("client", ex.Message); + } - [Fact] - public void AddDaprSecretStore_WithoutClient_ReportsError() + [Fact] + public void AddDaprSecretStore_DuplicateSecret_ReportsError() + { + var httpClient = new TestHttpClient() { - var ex = Assert.Throws(() => + Handler = async (entry) => { - CreateBuilder() - .AddDaprSecretStore("store", null) - .Build(); - }); + var secrets = new Dictionary() { { "secretName", "secret" }, { "SecretName", "secret" } }; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - Assert.Contains("client", ex.Message); - } + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - [Fact] - public void AddDaprSecretStore_DuplicateSecret_ReportsError() + var ex = Assert.Throws(() => { - var httpClient = new TestHttpClient() - { - Handler = async (entry) => - { - var secrets = new Dictionary() { { "secretName", "secret" }, { "SecretName", "secret" } }; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; - - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + CreateBuilder() + .AddDaprSecretStore("store", daprClient) .Build(); + }); - var ex = Assert.Throws(() => - { - CreateBuilder() - .AddDaprSecretStore("store", daprClient) - .Build(); - }); - - Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); - } + Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); + } - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatReturnsOneValue() + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatReturnsOneValue() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => - { - var secrets = new Dictionary() { { "secretName", "secret" } }; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; - - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var secrets = new Dictionary() { { "secretName", "secret" } }; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - config["secretName"].ShouldBe("secret"); - } + var config = CreateBuilder() + .AddDaprSecretStore("store", daprClient) + .Build(); + + config["secretName"].ShouldBe("secret"); + } - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatCanReturnsMultipleValues() + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatCanReturnsMultipleValues() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => - { - var secrets = new Dictionary() { - { "first_secret", "secret1" }, - { "second_secret", "secret2" }}; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; - - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var secrets = new Dictionary() { + { "first_secret", "secret1" }, + { "second_secret", "secret2" }}; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - config["first_secret"].ShouldBe("secret1"); - config["second_secret"].ShouldBe("secret2"); - } + var config = CreateBuilder() + .AddDaprSecretStore("store", daprClient) + .Build(); + + config["first_secret"].ShouldBe("secret1"); + config["second_secret"].ShouldBe("secret2"); + } - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsNonNormalizedKey() + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsNonNormalizedKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => - { - var secrets = new Dictionary() { { "secretName__value", "secret" } }; - await SendResponseWithSecrets(secrets, entry); - } - }; + var secrets = new Dictionary() { { "secretName__value", "secret" } }; + await SendResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }, daprClient) - .Build(); + var config = CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }, daprClient) + .Build(); - config["secretName:value"].ShouldBe("secret"); - } + config["secretName:value"].ShouldBe("secret"); + } - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatReturnsNonNormalizedKey() + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatReturnsNonNormalizedKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => - { - var secrets = new Dictionary() { - { "first_secret__value", "secret1" }}; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; + var secrets = new Dictionary() { + { "first_secret__value", "secret1" }}; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient) - .Build(); + var config = CreateBuilder() + .AddDaprSecretStore("store", daprClient) + .Build(); - config["first_secret:value"].ShouldBe("secret1"); - } + config["first_secret:value"].ShouldBe("secret1"); + } - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsNonNormalizedKeyDisabledNormalizeKey() + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsNonNormalizedKeyDisabledNormalizeKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => - { - var secrets = new Dictionary() { { "secretName__value", "secret" } }; - await SendResponseWithSecrets(secrets, entry); - } - }; + var secrets = new Dictionary() { { "secretName__value", "secret" } }; + await SendResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }; - conf.Client = daprClient; - conf.NormalizeKey = false; - }) - .Build(); - - config["secretName__value"].ShouldBe("secret"); - } + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }; + conf.Client = daprClient; + conf.NormalizeKey = false; + }) + .Build(); - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatReturnsNonNormalizedKeyDisabledNormalizeKey() + config["secretName__value"].ShouldBe("secret"); + } + + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatReturnsNonNormalizedKeyDisabledNormalizeKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => - { - var secrets = new Dictionary() { - { "first_secret__value", "secret1" }}; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; + var secrets = new Dictionary() { + { "first_secret__value", "secret1" }}; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.Client = daprClient; - conf.NormalizeKey = false; - }) - .Build(); + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.Client = daprClient; + conf.NormalizeKey = false; + }) + .Build(); - config["first_secret__value"].ShouldBe("secret1"); - } + config["first_secret__value"].ShouldBe("secret1"); + } - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey() + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["secretName--value"] = "secret", - }; - await SendResponseWithSecrets(secrets, entry); - } - }; + ["secretName--value"] = "secret", + }; + await SendResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.Client = daprClient; - conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; - conf.KeyDelimiters = new[] { "--" }; - }) - .Build(); - - config["secretName:value"].ShouldBe("secret"); - } + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.Client = daprClient; + conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; + conf.KeyDelimiters = new[] { "--" }; + }) + .Build(); - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey_NullDelimiters() + config["secretName:value"].ShouldBe("secret"); + } + + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey_NullDelimiters() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["secretName--value"] = "secret", - }; - await SendResponseWithSecrets(secrets, entry); - } - }; + ["secretName--value"] = "secret", + }; + await SendResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.Client = daprClient; - conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; - conf.KeyDelimiters = null; - }) - .Build(); - - config["secretName--value"].ShouldBe("secret"); - } + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.Client = daprClient; + conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; + conf.KeyDelimiters = null; + }) + .Build(); + + config["secretName--value"].ShouldBe("secret"); + } - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey_EmptyDelimiters() + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey_EmptyDelimiters() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["secretName--value"] = "secret", - }; - await SendResponseWithSecrets(secrets, entry); - } - }; + ["secretName--value"] = "secret", + }; + await SendResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.Client = daprClient; - conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; - conf.KeyDelimiters = new List(); - }) - .Build(); - - config["secretName--value"].ShouldBe("secret"); - } + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.Client = daprClient; + conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; + conf.KeyDelimiters = new List(); + }) + .Build(); - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsDifferentCustomDelimitedKeys() + config["secretName--value"].ShouldBe("secret"); + } + + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsDifferentCustomDelimitedKeys() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + // The following is an attempt at handling multiple secret descriptors for unit tests. + if (entry.Request.RequestUri.AbsoluteUri.Contains("healthz")) { - // The following is an attempt at handling multiple secret descriptors for unit tests. - if (entry.Request.RequestUri.AbsoluteUri.Contains("healthz")) + await SendEmptyResponse(entry); + } + else + { + var content = await entry.Request.Content.ReadAsStringAsync(); + if (content.Contains("secretName--value")) { - await SendEmptyResponse(entry); + await SendResponseWithSecrets(new Dictionary() + { + ["secretName--value"] = "secret", + }, entry); } - else + else if (content.Contains("otherSecretName≡value")) { - var content = await entry.Request.Content.ReadAsStringAsync(); - if (content.Contains("secretName--value")) - { - await SendResponseWithSecrets(new Dictionary() - { - ["secretName--value"] = "secret", - }, entry); - } - else if (content.Contains("otherSecretName≡value")) + await SendResponseWithSecrets(new Dictionary() { - await SendResponseWithSecrets(new Dictionary() - { - ["otherSecretName≡value"] = "secret", - }, entry); - } + ["otherSecretName≡value"] = "secret", + }, entry); } } - }; + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.Client = daprClient; - conf.SecretDescriptors = new DaprSecretDescriptor[] - { - new DaprSecretDescriptor("secretName--value"), - new DaprSecretDescriptor("otherSecretName≡value"), - }; - conf.KeyDelimiters = new[] { "--", "≡" }; - }) - .Build(); - - config["secretName:value"].ShouldBe("secret"); - config["otherSecretName:value"].ShouldBe("secret"); - } + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.Client = daprClient; + conf.SecretDescriptors = new DaprSecretDescriptor[] + { + new DaprSecretDescriptor("secretName--value"), + new DaprSecretDescriptor("otherSecretName≡value"), + }; + conf.KeyDelimiters = new[] { "--", "≡" }; + }) + .Build(); + + config["secretName:value"].ShouldBe("secret"); + config["otherSecretName:value"].ShouldBe("secret"); + } - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey() + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["first_secret--value"] = "secret1" - }; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; + ["first_secret--value"] = "secret1" + }; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient, new[] { "--" }) - .Build(); + var config = CreateBuilder() + .AddDaprSecretStore("store", daprClient, new[] { "--" }) + .Build(); - config["first_secret:value"].ShouldBe("secret1"); - } + config["first_secret:value"].ShouldBe("secret1"); + } - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatReturnsDifferentCustomDelimitedKey() + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatReturnsDifferentCustomDelimitedKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["first_secret--value"] = "secret1", - ["second_secret≡value"] = "secret2", - }; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; + ["first_secret--value"] = "secret1", + ["second_secret≡value"] = "secret2", + }; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) - .Build(); + var config = CreateBuilder() + .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) + .Build(); - config["first_secret:value"].ShouldBe("secret1"); - config["second_secret:value"].ShouldBe("secret2"); - } + config["first_secret:value"].ShouldBe("secret1"); + config["second_secret:value"].ShouldBe("secret2"); + } - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatReturnsPlainAndCustomDelimitedKey() + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatReturnsPlainAndCustomDelimitedKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["first_secret:value"] = "secret1", - ["second_secret--value"] = "secret2", - ["third_secret≡value"] = "secret3", - }; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; + ["first_secret:value"] = "secret1", + ["second_secret--value"] = "secret2", + ["third_secret≡value"] = "secret3", + }; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) - .Build(); + var config = CreateBuilder() + .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) + .Build(); - config["first_secret:value"].ShouldBe("secret1"); - config["second_secret:value"].ShouldBe("secret2"); - config["third_secret:value"].ShouldBe("secret3"); - } + config["first_secret:value"].ShouldBe("secret1"); + config["second_secret:value"].ShouldBe("secret2"); + config["third_secret:value"].ShouldBe("secret3"); + } - [Fact] - public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKeyDisabledNormalizeKey() + [Fact] + public void LoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKeyDisabledNormalizeKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["secretName--value"] = "secret" - }; - await SendResponseWithSecrets(secrets, entry); - } - }; + ["secretName--value"] = "secret" + }; + await SendResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.Client = daprClient; - conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; - conf.NormalizeKey = false; - conf.KeyDelimiters = new[] { "--" }; - }) - .Build(); - - config["secretName--value"].ShouldBe("secret"); - } + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.Client = daprClient; + conf.SecretDescriptors = new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName--value") }; + conf.NormalizeKey = false; + conf.KeyDelimiters = new[] { "--" }; + }) + .Build(); + + config["secretName--value"].ShouldBe("secret"); + } - [Fact] - public void BulkLoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKeyDisabledNormalizeKey() + [Fact] + public void BulkLoadSecrets_FromSecretStoreThatReturnsCustomDelimitedKeyDisabledNormalizeKey() + { + // Configure Client + var httpClient = new TestHttpClient() { - // Configure Client - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => + var secrets = new Dictionary() { - var secrets = new Dictionary() - { - ["first_secret--value"] = "secret1" - }; - await SendBulkResponseWithSecrets(secrets, entry); - } - }; + ["first_secret--value"] = "secret1" + }; + await SendBulkResponseWithSecrets(secrets, entry); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - var config = CreateBuilder() - .AddDaprSecretStore((conf) => - { - conf.Store = "store"; - conf.Client = daprClient; - conf.NormalizeKey = false; - conf.KeyDelimiters = new[] { "--" }; - }) - .Build(); - - config["first_secret--value"].ShouldBe("secret1"); - } + var config = CreateBuilder() + .AddDaprSecretStore((conf) => + { + conf.Store = "store"; + conf.Client = daprClient; + conf.NormalizeKey = false; + conf.KeyDelimiters = new[] { "--" }; + }) + .Build(); + + config["first_secret--value"].ShouldBe("secret1"); + } - [Fact] - public void LoadSecrets_FailsIfSidecarNotAvailable() + [Fact] + public void LoadSecrets_FailsIfSidecarNotAvailable() + { + var httpClient = new TestHttpClient() { - var httpClient = new TestHttpClient() + Handler = async (entry) => { - Handler = async (entry) => - { - await SendEmptyResponse(entry, HttpStatusCode.InternalServerError); - } - }; + await SendEmptyResponse(entry, HttpStatusCode.InternalServerError); + } + }; - var daprClient = new DaprClientBuilder() - .UseHttpClientFactory(() => httpClient) - .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) - .Build(); + var daprClient = new DaprClientBuilder() + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) + .Build(); - Assert.Throws(() => CreateBuilder() - .AddDaprSecretStore("store", daprClient, TimeSpan.FromMilliseconds(1)) - .Build()); - } + Assert.Throws(() => CreateBuilder() + .AddDaprSecretStore("store", daprClient, TimeSpan.FromMilliseconds(1)) + .Build()); + } - private IConfigurationBuilder CreateBuilder() - { - return new ConfigurationBuilder(); - } + private IConfigurationBuilder CreateBuilder() + { + return new ConfigurationBuilder(); + } - private async Task SendResponseWithSecrets(Dictionary secrets, TestHttpClient.Entry entry) - { - var secretResponse = new Autogenerated.GetSecretResponse(); - secretResponse.Data.Add(secrets); + private async Task SendResponseWithSecrets(Dictionary secrets, TestHttpClient.Entry entry) + { + var secretResponse = new Autogenerated.GetSecretResponse(); + secretResponse.Data.Add(secrets); - var streamContent = await GrpcUtils.CreateResponseContent(secretResponse); - var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); - entry.Completion.SetResult(response); - } + var streamContent = await GrpcUtils.CreateResponseContent(secretResponse); + var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); + entry.Completion.SetResult(response); + } - private async Task SendBulkResponseWithSecrets(Dictionary secrets, TestHttpClient.Entry entry) + private async Task SendBulkResponseWithSecrets(Dictionary secrets, TestHttpClient.Entry entry) + { + var getBulkSecretResponse = new Autogenerated.GetBulkSecretResponse(); + foreach (var secret in secrets) { - var getBulkSecretResponse = new Autogenerated.GetBulkSecretResponse(); - foreach (var secret in secrets) - { - var secretsResponse = new Autogenerated.SecretResponse(); - secretsResponse.Secrets[secret.Key] = secret.Value; - // Bulk secret response is `MapField>`. The outer key (string) must be ignored by `DaprSecretStoreConfigurationProvider`. - getBulkSecretResponse.Data.Add("IgnoredKey" + secret.Key, secretsResponse); - } - - var streamContent = await GrpcUtils.CreateResponseContent(getBulkSecretResponse); - var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); - entry.Completion.SetResult(response); + var secretsResponse = new Autogenerated.SecretResponse(); + secretsResponse.Secrets[secret.Key] = secret.Value; + // Bulk secret response is `MapField>`. The outer key (string) must be ignored by `DaprSecretStoreConfigurationProvider`. + getBulkSecretResponse.Data.Add("IgnoredKey" + secret.Key, secretsResponse); } - private async Task SendEmptyResponse(TestHttpClient.Entry entry, HttpStatusCode code = HttpStatusCode.OK) - { - var response = new Autogenerated.GetSecretResponse(); - var streamContent = await GrpcUtils.CreateResponseContent(response); - entry.Completion.SetResult(GrpcUtils.CreateResponse(code, streamContent)); - } + var streamContent = await GrpcUtils.CreateResponseContent(getBulkSecretResponse); + var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); + entry.Completion.SetResult(response); + } + + private async Task SendEmptyResponse(TestHttpClient.Entry entry, HttpStatusCode code = HttpStatusCode.OK) + { + var response = new Autogenerated.GetSecretResponse(); + var streamContent = await GrpcUtils.CreateResponseContent(response); + entry.Completion.SetResult(GrpcUtils.CreateResponse(code, streamContent)); } -} +} \ No newline at end of file diff --git a/test/Dapr.Extensions.Configuration.Test/TestHttpClient.cs b/test/Dapr.Extensions.Configuration.Test/TestHttpClient.cs index 0a7e2b7a8..89c81a46e 100644 --- a/test/Dapr.Extensions.Configuration.Test/TestHttpClient.cs +++ b/test/Dapr.Extensions.Configuration.Test/TestHttpClient.cs @@ -11,105 +11,104 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +namespace Dapr; + +using System; +using System.Collections.Concurrent; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +// This is an old piece of infrastructure with some limitations, don't use it in new places. +public class TestHttpClient : HttpClient { - using System; - using System.Collections.Concurrent; - using System.Net; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Text.Json; - using System.Threading; - using System.Threading.Tasks; - - // This is an old piece of infrastructure with some limitations, don't use it in new places. - public class TestHttpClient : HttpClient + private readonly TestHttpClientHandler handler; + + public TestHttpClient() + : this(new TestHttpClientHandler()) { - private readonly TestHttpClientHandler handler; + } - public TestHttpClient() - : this(new TestHttpClientHandler()) - { - } + private TestHttpClient(TestHttpClientHandler handler) + : base(handler) + { + this.handler = handler; + } - private TestHttpClient(TestHttpClientHandler handler) - : base(handler) - { - this.handler = handler; - } + public ConcurrentQueue Requests => this.handler.Requests; - public ConcurrentQueue Requests => this.handler.Requests; + public Action Handler + { + get => this.handler.Handler; + set => this.handler.Handler = value; + } - public Action Handler + public class Entry + { + public Entry(HttpRequestMessage request) { - get => this.handler.Handler; - set => this.handler.Handler = value; + this.Request = request; + + this.Completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } - public class Entry - { - public Entry(HttpRequestMessage request) - { - this.Request = request; + public TaskCompletionSource Completion { get; } - this.Completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - } + public HttpRequestMessage Request { get; } - public TaskCompletionSource Completion { get; } - - public HttpRequestMessage Request { get; } + public void Respond(HttpResponseMessage response) + { + this.Completion.SetResult(response); + } - public void Respond(HttpResponseMessage response) - { - this.Completion.SetResult(response); - } + public void RespondWithResponse(HttpResponseMessage response) + { + this.Completion.SetResult(response); + } - public void RespondWithResponse(HttpResponseMessage response) - { - this.Completion.SetResult(response); - } + public void RespondWithJson(TValue value, JsonSerializerOptions options = null) + { + var bytes = JsonSerializer.SerializeToUtf8Bytes(value, options); - public void RespondWithJson(TValue value, JsonSerializerOptions options = null) + var response = new HttpResponseMessage(HttpStatusCode.OK) { - var bytes = JsonSerializer.SerializeToUtf8Bytes(value, options); - - var response = new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new ByteArrayContent(bytes) - }; - response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "UTF-8", }; + Content = new ByteArrayContent(bytes) + }; + response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "UTF-8", }; - this.Completion.SetResult(response); - } + this.Completion.SetResult(response); + } - public void Throw(Exception exception) - { - this.Completion.SetException(exception); - } + public void Throw(Exception exception) + { + this.Completion.SetException(exception); } + } - private class TestHttpClientHandler : HttpMessageHandler + private class TestHttpClientHandler : HttpMessageHandler + { + public TestHttpClientHandler() { - public TestHttpClientHandler() - { - this.Requests = new ConcurrentQueue(); - } + this.Requests = new ConcurrentQueue(); + } - public ConcurrentQueue Requests { get; } + public ConcurrentQueue Requests { get; } - public Action Handler { get; set; } + public Action Handler { get; set; } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var entry = new Entry(request); + this.Handler?.Invoke(entry); + this.Requests.Enqueue(entry); - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + using (cancellationToken.Register(() => entry.Completion.TrySetCanceled())) { - var entry = new Entry(request); - this.Handler?.Invoke(entry); - this.Requests.Enqueue(entry); - - using (cancellationToken.Register(() => entry.Completion.TrySetCanceled())) - { - return await entry.Completion.Task.ConfigureAwait(false); - } + return await entry.Completion.Task.ConfigureAwait(false); } } } -} +} \ No newline at end of file diff --git a/test/Dapr.Messaging.Test/PublishSubscribe/MessageHandlingPolicyTest.cs b/test/Dapr.Messaging.Test/PublishSubscribe/MessageHandlingPolicyTest.cs index 6efdd6397..47dc68976 100644 --- a/test/Dapr.Messaging.Test/PublishSubscribe/MessageHandlingPolicyTest.cs +++ b/test/Dapr.Messaging.Test/PublishSubscribe/MessageHandlingPolicyTest.cs @@ -13,56 +13,55 @@ using Dapr.Messaging.PublishSubscribe; -namespace Dapr.Messaging.Test.PublishSubscribe +namespace Dapr.Messaging.Test.PublishSubscribe; + +public class MessageHandlingPolicyTest { - public class MessageHandlingPolicyTest + [Fact] + public void Test_MessageHandlingPolicy_Constructor() { - [Fact] - public void Test_MessageHandlingPolicy_Constructor() - { - var timeoutDuration = TimeSpan.FromMilliseconds(2000); - const TopicResponseAction defaultResponseAction = TopicResponseAction.Drop; + var timeoutDuration = TimeSpan.FromMilliseconds(2000); + const TopicResponseAction defaultResponseAction = TopicResponseAction.Drop; - var policy = new MessageHandlingPolicy(timeoutDuration, defaultResponseAction); + var policy = new MessageHandlingPolicy(timeoutDuration, defaultResponseAction); - Assert.Equal(timeoutDuration, policy.TimeoutDuration); - Assert.Equal(defaultResponseAction, policy.DefaultResponseAction); - } + Assert.Equal(timeoutDuration, policy.TimeoutDuration); + Assert.Equal(defaultResponseAction, policy.DefaultResponseAction); + } - [Fact] - public void Test_MessageHandlingPolicy_Equality() - { - var timeSpan1 = TimeSpan.FromMilliseconds(1000); - var timeSpan2 = TimeSpan.FromMilliseconds(2000); + [Fact] + public void Test_MessageHandlingPolicy_Equality() + { + var timeSpan1 = TimeSpan.FromMilliseconds(1000); + var timeSpan2 = TimeSpan.FromMilliseconds(2000); - var policy1 = new MessageHandlingPolicy(timeSpan1, TopicResponseAction.Success); - var policy2 = new MessageHandlingPolicy(timeSpan1, TopicResponseAction.Success); - var policy3 = new MessageHandlingPolicy(timeSpan2, TopicResponseAction.Retry); + var policy1 = new MessageHandlingPolicy(timeSpan1, TopicResponseAction.Success); + var policy2 = new MessageHandlingPolicy(timeSpan1, TopicResponseAction.Success); + var policy3 = new MessageHandlingPolicy(timeSpan2, TopicResponseAction.Retry); - Assert.Equal(policy1, policy2); // Value Equality - Assert.NotEqual(policy1, policy3); // Different values - } + Assert.Equal(policy1, policy2); // Value Equality + Assert.NotEqual(policy1, policy3); // Different values + } - [Fact] - public void Test_MessageHandlingPolicy_Immutability() - { - var timeoutDuration = TimeSpan.FromMilliseconds(2000); - const TopicResponseAction defaultResponseAction = TopicResponseAction.Drop; + [Fact] + public void Test_MessageHandlingPolicy_Immutability() + { + var timeoutDuration = TimeSpan.FromMilliseconds(2000); + const TopicResponseAction defaultResponseAction = TopicResponseAction.Drop; - var policy1 = new MessageHandlingPolicy(timeoutDuration, defaultResponseAction); + var policy1 = new MessageHandlingPolicy(timeoutDuration, defaultResponseAction); - var newTimeoutDuration = TimeSpan.FromMilliseconds(3000); - const TopicResponseAction newDefaultResponseAction = TopicResponseAction.Retry; + var newTimeoutDuration = TimeSpan.FromMilliseconds(3000); + const TopicResponseAction newDefaultResponseAction = TopicResponseAction.Retry; - // Creating a new policy with different values. - var policy2 = policy1 with - { - TimeoutDuration = newTimeoutDuration, DefaultResponseAction = newDefaultResponseAction - }; + // Creating a new policy with different values. + var policy2 = policy1 with + { + TimeoutDuration = newTimeoutDuration, DefaultResponseAction = newDefaultResponseAction + }; - // Asserting that original policy is unaffected by changes made to new policy. - Assert.Equal(timeoutDuration, policy1.TimeoutDuration); - Assert.Equal(defaultResponseAction, policy1.DefaultResponseAction); - } + // Asserting that original policy is unaffected by changes made to new policy. + Assert.Equal(timeoutDuration, policy1.TimeoutDuration); + Assert.Equal(defaultResponseAction, policy1.DefaultResponseAction); } -} +} \ No newline at end of file diff --git a/test/Dapr.Workflow.Test/WorkflowActivityTest.cs b/test/Dapr.Workflow.Test/WorkflowActivityTest.cs index f6be41fc7..895a746d0 100644 --- a/test/Dapr.Workflow.Test/WorkflowActivityTest.cs +++ b/test/Dapr.Workflow.Test/WorkflowActivityTest.cs @@ -11,45 +11,44 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Workflow.Test +namespace Dapr.Workflow.Test; + +using Moq; +using System.Threading.Tasks; +using Xunit; +using Xunit.Sdk; + +/// +/// Contains tests for WorkflowActivityContext. +/// +public class WorkflowActivityTest { - using Moq; - using System.Threading.Tasks; - using Xunit; - using Xunit.Sdk; - - /// - /// Contains tests for WorkflowActivityContext. - /// - public class WorkflowActivityTest - { - private IWorkflowActivity workflowActivity; + private IWorkflowActivity workflowActivity; - private Mock workflowActivityContextMock; + private Mock workflowActivityContextMock; - public WorkflowActivityTest() - { - this.workflowActivity = new TestDaprWorkflowActivity(); - this.workflowActivityContextMock = new Mock(); - } + public WorkflowActivityTest() + { + this.workflowActivity = new TestDaprWorkflowActivity(); + this.workflowActivityContextMock = new Mock(); + } - [Fact] - public async Task RunAsync_ShouldReturnCorrectContextInstanceId() - { - this.workflowActivityContextMock.Setup((x) => x.InstanceId).Returns("instanceId"); + [Fact] + public async Task RunAsync_ShouldReturnCorrectContextInstanceId() + { + this.workflowActivityContextMock.Setup((x) => x.InstanceId).Returns("instanceId"); - string result = (string) (await this.workflowActivity.RunAsync(this.workflowActivityContextMock.Object, "input"))!; + string result = (string) (await this.workflowActivity.RunAsync(this.workflowActivityContextMock.Object, "input"))!; - Assert.Equal("instanceId", result); - } + Assert.Equal("instanceId", result); + } - public class TestDaprWorkflowActivity : WorkflowActivity + public class TestDaprWorkflowActivity : WorkflowActivity + { + public override Task RunAsync(WorkflowActivityContext context, string input) { - public override Task RunAsync(WorkflowActivityContext context, string input) - { - return Task.FromResult(context.InstanceId); - } + return Task.FromResult(context.InstanceId); } } -} +} \ No newline at end of file diff --git a/test/Shared/AppCallbackClient.cs b/test/Shared/AppCallbackClient.cs index 5454e576f..71fc5705f 100644 --- a/test/Shared/AppCallbackClient.cs +++ b/test/Shared/AppCallbackClient.cs @@ -11,66 +11,65 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr +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 { - 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; + public AppCallbackClient(AppCallbackBase callbackService) + : base(new Handler(callbackService)) + { + } - // This client will forward requests to the AppCallback service implementation which then responds to the request - public class AppCallbackClient : HttpClient + private class Handler : HttpMessageHandler { - public AppCallbackClient(AppCallbackBase callbackService) - : base(new Handler(callbackService)) + private readonly AppCallbackBase callbackService; + + public Handler(AppCallbackBase callbackService) { + this.callbackService = callbackService; } - private class Handler : HttpMessageHandler + protected override async Task SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken) { - private readonly AppCallbackBase callbackService; - - public Handler(AppCallbackBase callbackService) + var metadata = new Metadata(); + foreach (var (key, value) in httpRequest.Headers) { - this.callbackService = callbackService; + metadata.Add(key, string.Join(",", value.ToArray())); } - protected override async Task 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 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(httpRequest); - var grpcResponse = await this.callbackService.OnInvoke(grpcRequest.Message, context); + var grpcRequest = await GrpcUtils.GetRequestFromRequestMessageAsync(httpRequest); + var grpcResponse = await this.callbackService.OnInvoke(grpcRequest.Message, context); - var streamContent = await GrpcUtils.CreateResponseContent(grpcResponse); - var httpResponse = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); + var streamContent = await GrpcUtils.CreateResponseContent(grpcResponse); + var httpResponse = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); - return httpResponse; - } + return httpResponse; } } -} +} \ No newline at end of file From 1ffb3d006d25657b362fea9599a74122d2c251a3 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 06:16:17 -0500 Subject: [PATCH 05/12] Using target-typed new expression Signed-off-by: Whit Waldo --- src/Dapr.Jobs/Extensions/DaprSerializationExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dapr.Jobs/Extensions/DaprSerializationExtensions.cs b/src/Dapr.Jobs/Extensions/DaprSerializationExtensions.cs index 1f02a32cd..0a612e3a9 100644 --- a/src/Dapr.Jobs/Extensions/DaprSerializationExtensions.cs +++ b/src/Dapr.Jobs/Extensions/DaprSerializationExtensions.cs @@ -25,7 +25,7 @@ public static class DaprJobsSerializationExtensions /// /// Default JSON serializer options. /// - private static readonly JsonSerializerOptions defaultOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); + private static readonly JsonSerializerOptions defaultOptions = new(JsonSerializerDefaults.Web); /// /// Schedules a job with Dapr. From 92f4b0e9735272229b382a618788f895c19663ef Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 06:27:27 -0500 Subject: [PATCH 06/12] QoL improvements to code base - primary constructors on classes and records, use of collection initializers, target-typed new expressions, etc. Signed-off-by: Whit Waldo --- .../DaprWorkflowActivityContext.cs | 2 +- src/Dapr.Workflow/DaprWorkflowClient.cs | 19 ++--- .../DaprWorkflowClientBuilderFactory.cs | 14 +--- src/Dapr.Workflow/WorkflowLoggingService.cs | 25 +++--- src/Dapr.Workflow/WorkflowRetryPolicy.cs | 81 +++++++++---------- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 4 +- .../WorkflowTaskFailedException.cs | 20 ++--- src/Dapr.Workflow/WorkflowTaskOptions.cs | 22 +---- 8 files changed, 66 insertions(+), 121 deletions(-) diff --git a/src/Dapr.Workflow/DaprWorkflowActivityContext.cs b/src/Dapr.Workflow/DaprWorkflowActivityContext.cs index acd90f008..27aab2537 100644 --- a/src/Dapr.Workflow/DaprWorkflowActivityContext.cs +++ b/src/Dapr.Workflow/DaprWorkflowActivityContext.cs @@ -33,4 +33,4 @@ internal DaprWorkflowActivityContext(TaskActivityContext innerContext) /// public override string InstanceId => this.innerContext.InstanceId; -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/DaprWorkflowClient.cs b/src/Dapr.Workflow/DaprWorkflowClient.cs index 6f9e05bde..add9bbac6 100644 --- a/src/Dapr.Workflow/DaprWorkflowClient.cs +++ b/src/Dapr.Workflow/DaprWorkflowClient.cs @@ -13,7 +13,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using Dapr.Client; using Microsoft.DurableTask; using Microsoft.DurableTask.Client; @@ -26,19 +25,11 @@ namespace Dapr.Workflow; /// This is an alternative to the general purpose Dapr client. It uses a gRPC connection to send /// commands directly to the workflow engine, bypassing the Dapr API layer. /// -public class DaprWorkflowClient : IAsyncDisposable +/// The Durable Task client used to communicate with the Dapr sidecar. +/// Thrown if is null. +public class DaprWorkflowClient(DurableTaskClient innerClient) : IAsyncDisposable { - readonly DurableTaskClient innerClient; - - /// - /// Initializes a new instance of the class. - /// - /// The Durable Task client used to communicate with the Dapr sidecar. - /// Thrown if is null. - public DaprWorkflowClient(DurableTaskClient innerClient) - { - this.innerClient = innerClient ?? throw new ArgumentNullException(nameof(innerClient)); - } + readonly DurableTaskClient innerClient = innerClient ?? throw new ArgumentNullException(nameof(innerClient)); /// /// Schedules a new workflow instance for execution. @@ -299,4 +290,4 @@ public ValueTask DisposeAsync() { return ((IAsyncDisposable)this.innerClient).DisposeAsync(); } -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs index 760dadd8b..32d70907a 100644 --- a/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs +++ b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs @@ -24,20 +24,8 @@ namespace Dapr.Workflow; /// /// A factory for building a . /// -internal sealed class DaprWorkflowClientBuilderFactory +internal sealed class DaprWorkflowClientBuilderFactory(IConfiguration? configuration, IHttpClientFactory httpClientFactory) { - private readonly IConfiguration? configuration; - private readonly IHttpClientFactory httpClientFactory; - - /// - /// Constructor used to inject the required types into the factory. - /// - public DaprWorkflowClientBuilderFactory(IConfiguration? configuration, IHttpClientFactory httpClientFactory) - { - this.configuration = configuration; - this.httpClientFactory = httpClientFactory; - } - /// /// Responsible for building the client itself. /// diff --git a/src/Dapr.Workflow/WorkflowLoggingService.cs b/src/Dapr.Workflow/WorkflowLoggingService.cs index 5b8521b29..a4c4b3e75 100644 --- a/src/Dapr.Workflow/WorkflowLoggingService.cs +++ b/src/Dapr.Workflow/WorkflowLoggingService.cs @@ -23,30 +23,25 @@ namespace Dapr.Workflow; /// /// Defines runtime options for workflows. /// -internal sealed class WorkflowLoggingService : IHostedService +internal sealed class WorkflowLoggingService(ILogger logger) : IHostedService { - private readonly ILogger logger; - private static readonly HashSet registeredWorkflows = new(); - private static readonly HashSet registeredActivities = new(); + private static readonly HashSet registeredWorkflows = []; + private static readonly HashSet registeredActivities = []; - public WorkflowLoggingService(ILogger logger) - { - this.logger = logger; - } public Task StartAsync(CancellationToken cancellationToken) { - this.logger.Log(LogLevel.Information, "WorkflowLoggingService started"); + logger.Log(LogLevel.Information, "WorkflowLoggingService started"); - this.logger.Log(LogLevel.Information, "List of registered workflows"); + logger.Log(LogLevel.Information, "List of registered workflows"); foreach (string item in registeredWorkflows) { - this.logger.Log(LogLevel.Information, item); + logger.Log(LogLevel.Information, item); } - this.logger.Log(LogLevel.Information, "List of registered activities:"); + logger.Log(LogLevel.Information, "List of registered activities:"); foreach (string item in registeredActivities) { - this.logger.Log(LogLevel.Information, item); + logger.Log(LogLevel.Information, item); } return Task.CompletedTask; @@ -54,7 +49,7 @@ public Task StartAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken) { - this.logger.Log(LogLevel.Information, "WorkflowLoggingService stopped"); + logger.Log(LogLevel.Information, "WorkflowLoggingService stopped"); return Task.CompletedTask; } @@ -69,4 +64,4 @@ public static void LogActivityName(string activityName) registeredActivities.Add(activityName); } -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/WorkflowRetryPolicy.cs b/src/Dapr.Workflow/WorkflowRetryPolicy.cs index d20fe20a0..d69fe0fa8 100644 --- a/src/Dapr.Workflow/WorkflowRetryPolicy.cs +++ b/src/Dapr.Workflow/WorkflowRetryPolicy.cs @@ -10,6 +10,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ + using System; using System.Threading; using Microsoft.DurableTask; @@ -19,50 +20,42 @@ namespace Dapr.Workflow; /// /// A declarative retry policy that can be configured for activity or child workflow calls. /// -public class WorkflowRetryPolicy +/// The maximum number of task invocation attempts. Must be 1 or greater. +/// The amount of time to delay between the first and second attempt. +/// +/// The exponential back-off coefficient used to determine the delay between subsequent retries. Must be 1.0 or greater. +/// +/// +/// The maximum time to delay between attempts, regardless of. +/// +/// The overall timeout for retries. +/// +/// The value can be used to specify an unlimited timeout for +/// or . +/// +/// +/// Thrown if any of the following are true: +/// +/// The value for is less than or equal to zero. +/// The value for is less than or equal to . +/// The value for is less than 1.0. +/// The value for is less than . +/// The value for is less than . +/// +/// +public class WorkflowRetryPolicy( + int maxNumberOfAttempts, + TimeSpan firstRetryInterval, + double backoffCoefficient = 1.0, + TimeSpan? maxRetryInterval = null, + TimeSpan? retryTimeout = null) { - readonly RetryPolicy durableRetryPolicy; - - /// - /// Initializes a new instance of the class. - /// - /// The maximum number of task invocation attempts. Must be 1 or greater. - /// The amount of time to delay between the first and second attempt. - /// - /// The exponential back-off coefficient used to determine the delay between subsequent retries. Must be 1.0 or greater. - /// - /// - /// The maximum time to delay between attempts, regardless of. - /// - /// The overall timeout for retries. - /// - /// The value can be used to specify an unlimited timeout for - /// or . - /// - /// - /// Thrown if any of the following are true: - /// - /// The value for is less than or equal to zero. - /// The value for is less than or equal to . - /// The value for is less than 1.0. - /// The value for is less than . - /// The value for is less than . - /// - /// - public WorkflowRetryPolicy( - int maxNumberOfAttempts, - TimeSpan firstRetryInterval, - double backoffCoefficient = 1.0, - TimeSpan? maxRetryInterval = null, - TimeSpan? retryTimeout = null) - { - this.durableRetryPolicy = new RetryPolicy( - maxNumberOfAttempts, - firstRetryInterval, - backoffCoefficient, - maxRetryInterval, - retryTimeout); - } + private readonly RetryPolicy durableRetryPolicy = new( + maxNumberOfAttempts, + firstRetryInterval, + backoffCoefficient, + maxRetryInterval, + retryTimeout); /// /// Gets the max number of attempts for executing a given task. @@ -100,4 +93,4 @@ public WorkflowRetryPolicy( public TimeSpan RetryTimeout => this.durableRetryPolicy.RetryTimeout; internal RetryPolicy GetDurableRetryPolicy() => this.durableRetryPolicy; -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index a7906db2e..c030d0b1a 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -40,7 +40,7 @@ public sealed class WorkflowRuntimeOptions /// Initializes a new instance of the class. /// /// - /// Instances of this type are expected to be instanciated from a dependency injection container. + /// Instances of this type are expected to be instantiated from a dependency injection container. /// public WorkflowRuntimeOptions() { @@ -186,4 +186,4 @@ public ActivityWrapper(IWorkflowActivity activity) return this.activity.RunAsync(new DaprWorkflowActivityContext(context), input); } } -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/WorkflowTaskFailedException.cs b/src/Dapr.Workflow/WorkflowTaskFailedException.cs index 331793fee..cd7600f9c 100644 --- a/src/Dapr.Workflow/WorkflowTaskFailedException.cs +++ b/src/Dapr.Workflow/WorkflowTaskFailedException.cs @@ -18,21 +18,13 @@ namespace Dapr.Workflow; /// /// Exception type for Dapr Workflow task failures. /// -public class WorkflowTaskFailedException : Exception +/// The exception message. +/// Details about the failure. +public class WorkflowTaskFailedException(string message, WorkflowTaskFailureDetails failureDetails) + : Exception(message) { - /// - /// Initializes a new instance of the class. - /// - /// The exception message. - /// Details about the failure. - public WorkflowTaskFailedException(string message, WorkflowTaskFailureDetails failureDetails) - : base(message) - { - this.FailureDetails = failureDetails ?? throw new ArgumentNullException(nameof(failureDetails)); - } - /// /// Gets more information about the underlying workflow task failure. /// - public WorkflowTaskFailureDetails FailureDetails { get; } -} \ No newline at end of file + public WorkflowTaskFailureDetails FailureDetails { get; } = failureDetails ?? throw new ArgumentNullException(nameof(failureDetails)); +} diff --git a/src/Dapr.Workflow/WorkflowTaskOptions.cs b/src/Dapr.Workflow/WorkflowTaskOptions.cs index b020f23a2..7ab201a00 100644 --- a/src/Dapr.Workflow/WorkflowTaskOptions.cs +++ b/src/Dapr.Workflow/WorkflowTaskOptions.cs @@ -36,24 +36,10 @@ internal TaskOptions ToDurableTaskOptions() /// /// Options for controlling the behavior of child workflow execution. /// -public record ChildWorkflowTaskOptions : WorkflowTaskOptions +/// The instance ID to use for the child workflow. +/// The child workflow's retry policy. +public record ChildWorkflowTaskOptions(string? InstanceId = null, WorkflowRetryPolicy? RetryPolicy = null) : WorkflowTaskOptions(RetryPolicy) { - /// - /// Initializes a new instance of the record. - /// - /// The instance ID to use for the child workflow. - /// The child workflow's retry policy. - public ChildWorkflowTaskOptions(string? instanceId = null, WorkflowRetryPolicy ? retryPolicy = null) - : base(retryPolicy) - { - this.InstanceId = instanceId; - } - - /// - /// Gets the instance ID to use when creating a child workflow. - /// - public string? InstanceId { get; init; } - internal new SubOrchestrationOptions ToDurableTaskOptions() { TaskRetryOptions? retryOptions = null; @@ -64,4 +50,4 @@ public ChildWorkflowTaskOptions(string? instanceId = null, WorkflowRetryPolicy ? return new SubOrchestrationOptions(retryOptions, this.InstanceId); } -} \ No newline at end of file +} From c4110eecb9fdd626fb227f841317e5d8934618a1 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 06:27:46 -0500 Subject: [PATCH 07/12] Updated to use primary constructor Signed-off-by: Whit Waldo --- .../Conversation/DaprConversationClientBuilder.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Dapr.AI/Conversation/DaprConversationClientBuilder.cs b/src/Dapr.AI/Conversation/DaprConversationClientBuilder.cs index 88cf3b2a9..ae897fcde 100644 --- a/src/Dapr.AI/Conversation/DaprConversationClientBuilder.cs +++ b/src/Dapr.AI/Conversation/DaprConversationClientBuilder.cs @@ -20,16 +20,9 @@ namespace Dapr.AI.Conversation; /// /// Used to create a new instance of a . /// -public sealed class DaprConversationClientBuilder : DaprGenericClientBuilder +/// An optional to configure the client with. +public sealed class DaprConversationClientBuilder(IConfiguration? configuration = null) : DaprGenericClientBuilder(configuration) { - /// - /// Used to initialize a new instance of the . - /// - /// An optional to configure the client with. - public DaprConversationClientBuilder(IConfiguration? configuration = null) : base(configuration) - { - } - /// /// Builds the client instance from the properties of the builder. /// From 1effd276ba794aad29d12cb60ebfb29de2e4c639 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 08:38:27 -0500 Subject: [PATCH 08/12] Applied all manner of QoL improvements to Dapr.Client Signed-off-by: Whit Waldo --- src/Dapr.Client/BindingRequest.cs | 31 ++----- src/Dapr.Client/BindingResponse.cs | 32 +++---- src/Dapr.Client/BulkPublishEntry.cs | 37 +++----- src/Dapr.Client/BulkPublishResponse.cs | 16 +--- .../BulkPublishResponseFailedEntry.cs | 21 ++--- src/Dapr.Client/BulkStateItem.cs | 62 +++++-------- src/Dapr.Client/CloudEvent.cs | 14 +-- src/Dapr.Client/ConfigurationItem.cs | 26 ++---- src/Dapr.Client/CryptographyOptions.cs | 28 ++---- src/Dapr.Client/DaprClient.cs | 88 +++++++++---------- src/Dapr.Client/DaprClientGrpc.cs | 59 ++++++------- src/Dapr.Client/DaprMetadata.cs | 79 +++++------------ .../DaprSubscribeConfigurationSource.cs | 22 +---- src/Dapr.Client/DeleteBulkStateItem.cs | 32 +++---- src/Dapr.Client/GetConfigurationResponse.cs | 24 +---- src/Dapr.Client/InvocationHandler.cs | 31 +++---- src/Dapr.Client/InvocationInterceptor.cs | 22 ++--- src/Dapr.Client/SaveStateItem.cs | 39 +++----- src/Dapr.Client/StateEntry.cs | 70 ++++++--------- src/Dapr.Client/StateQueryException.cs | 24 ++--- src/Dapr.Client/StateQueryResponse.cs | 55 ++++-------- src/Dapr.Client/StateTransactionRequest.cs | 46 ++++------ .../SubscribeConfigurationResponse.cs | 24 +---- src/Dapr.Client/TryLockResponse.cs | 12 +-- src/Dapr.Client/TypeConverters.cs | 9 +- .../UnsubscribeConfigurationResponse.cs | 21 ++--- 26 files changed, 310 insertions(+), 614 deletions(-) diff --git a/src/Dapr.Client/BindingRequest.cs b/src/Dapr.Client/BindingRequest.cs index 46559471e..a12aab69d 100644 --- a/src/Dapr.Client/BindingRequest.cs +++ b/src/Dapr.Client/BindingRequest.cs @@ -19,36 +19,21 @@ namespace Dapr.Client; /// /// Represents the request used to invoke a binding. /// -public sealed class BindingRequest +/// The name of the binding. +/// The type of operation to perform on the binding. +public sealed class BindingRequest(string bindingName, string operation) { - /// - /// Initializes a new for the provided and - /// . - /// - /// The name of the binding. - /// The type of operation to perform on the binding. - public BindingRequest(string bindingName, string operation) - { - ArgumentVerifier.ThrowIfNullOrEmpty(bindingName, nameof(bindingName)); - ArgumentVerifier.ThrowIfNullOrEmpty(operation, nameof(operation)); - - this.BindingName = bindingName; - this.Operation = operation; - - this.Metadata = new Dictionary(); - } - /// /// Gets the name of the binding. /// /// - public string BindingName { get; } + public string BindingName { get; } = bindingName ?? throw new ArgumentNullException(nameof(bindingName)); /// /// Gets the type of operation to perform on the binding. /// - public string Operation { get; } - + public string Operation { get; } = operation ?? throw new ArgumentNullException(nameof(operation)); + /// /// Gets or sets the binding request payload. /// @@ -58,5 +43,5 @@ public BindingRequest(string bindingName, string operation) /// Gets the metadata; a collection of metadata key-value pairs that will be provided to the binding. /// The valid metadata keys and values are determined by the type of binding used. /// - public Dictionary Metadata { get; } -} \ No newline at end of file + public Dictionary Metadata { get; } = new(); +} diff --git a/src/Dapr.Client/BindingResponse.cs b/src/Dapr.Client/BindingResponse.cs index a134d3ac8..cf5be5b6f 100644 --- a/src/Dapr.Client/BindingResponse.cs +++ b/src/Dapr.Client/BindingResponse.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable using System; using System.Collections.Generic; @@ -19,37 +20,24 @@ namespace Dapr.Client; /// /// Represents the response from invoking a binding. /// -public sealed class BindingResponse +/// The assocated with this response. +/// The response payload. +/// The response metadata. +public sealed class BindingResponse(BindingRequest request, ReadOnlyMemory data, IReadOnlyDictionary metadata) { - /// - /// Initializes a new .` - /// - /// The assocated with this response. - /// The response payload. - /// The response metadata. - public BindingResponse(BindingRequest request, ReadOnlyMemory data, IReadOnlyDictionary metadata) - { - ArgumentVerifier.ThrowIfNull(request, nameof(request)); - ArgumentVerifier.ThrowIfNull(data, nameof(data)); - ArgumentVerifier.ThrowIfNull(metadata, nameof(metadata)); - - this.Request = request; - this.Data = data; - this.Metadata = metadata; - } - /// /// Gets the assocated with this response. /// - public BindingRequest Request { get; } + public BindingRequest Request { get; } = request ?? throw new ArgumentNullException(nameof(request)); /// /// Gets the response payload. /// - public ReadOnlyMemory Data { get; } + public ReadOnlyMemory Data { get; } = data; /// /// Gets the response metadata. /// - public IReadOnlyDictionary Metadata { get; } -} \ No newline at end of file + public IReadOnlyDictionary Metadata { get; } = + metadata ?? throw new ArgumentNullException(nameof(metadata)); +} diff --git a/src/Dapr.Client/BulkPublishEntry.cs b/src/Dapr.Client/BulkPublishEntry.cs index 8f9757aef..d4d9107fd 100644 --- a/src/Dapr.Client/BulkPublishEntry.cs +++ b/src/Dapr.Client/BulkPublishEntry.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable using System.Collections.Generic; namespace Dapr.Client; @@ -19,42 +20,30 @@ namespace Dapr.Client; /// Class representing an entry in the BulkPublishRequest. /// /// The data type of the value. -public class BulkPublishEntry +/// A request scoped ID uniquely identifying this entry in the BulkPublishRequest. +/// Event to be published. +/// Content Type of the event to be published. +/// Metadata for the event. +public class BulkPublishEntry(string entryId, TValue eventData, string contentType, IReadOnlyDictionary? metadata = null) { - /// - /// Initializes a new instance of the class. - /// - /// A request scoped ID uniquely identifying this entry in the BulkPublishRequest. - /// Event to be published. - /// Content Type of the event to be published. - /// Metadata for the event. - public BulkPublishEntry(string entryId, TValue eventData, string contentType, IReadOnlyDictionary metadata = default) - { - this.EntryId = entryId; - this.EventData = eventData; - this.ContentType = contentType; - this.Metadata = metadata; - } - /// /// The ID uniquely identifying this particular request entry across the request and scoped for this request only. /// - public string EntryId { get; } + public string EntryId { get; } = entryId; /// /// The event to be published. /// - public TValue EventData { get; } - + public TValue EventData { get; } = eventData; + /// /// The content type of the event to be published. /// - public string ContentType { get; } - + public string ContentType { get; } = contentType; + /// /// The metadata set for this particular event. /// Any particular values in this metadata overrides the request metadata present in BulkPublishRequest. /// - public IReadOnlyDictionary Metadata { get; } - -} \ No newline at end of file + public IReadOnlyDictionary? Metadata { get; } = metadata; +} diff --git a/src/Dapr.Client/BulkPublishResponse.cs b/src/Dapr.Client/BulkPublishResponse.cs index 602d3a334..2e227aafd 100644 --- a/src/Dapr.Client/BulkPublishResponse.cs +++ b/src/Dapr.Client/BulkPublishResponse.cs @@ -18,19 +18,11 @@ namespace Dapr.Client; /// /// Class representing the response returned on bulk publishing events. /// -public class BulkPublishResponse +/// The List of BulkPublishResponseEntries representing the list of events that failed to be published. +public class BulkPublishResponse(List> failedEntries) { - /// - /// Initializes a new instance of the class. - /// - /// The List of BulkPublishResponseEntries representing the list of events that failed to be published. - public BulkPublishResponse(List> failedEntries) - { - this.FailedEntries = failedEntries; - } - /// /// The List of BulkPublishResponseFailedEntry objects that have failed to publish. /// - public List> FailedEntries { get; } -} \ No newline at end of file + public List> FailedEntries { get; } = failedEntries; +} diff --git a/src/Dapr.Client/BulkPublishResponseFailedEntry.cs b/src/Dapr.Client/BulkPublishResponseFailedEntry.cs index 7357647ce..bc3d122e2 100644 --- a/src/Dapr.Client/BulkPublishResponseFailedEntry.cs +++ b/src/Dapr.Client/BulkPublishResponseFailedEntry.cs @@ -16,26 +16,17 @@ namespace Dapr.Client; /// /// Class representing the status of each event that was published using BulkPublishRequest. /// -public class BulkPublishResponseFailedEntry +/// The entry that failed to be published. +/// Error message as to why the entry failed to publish. +public class BulkPublishResponseFailedEntry(BulkPublishEntry entry, string errorMessage) { - /// - /// Initializes a new instance of the class. - /// - /// The entry that failed to be published. - /// Error message as to why the entry failed to publish. - public BulkPublishResponseFailedEntry(BulkPublishEntry entry, string errorMessage) - { - this.Entry = entry; - this.ErrorMessage = errorMessage; - } - /// /// The entry that has failed. /// - public BulkPublishEntry Entry { get; } + public BulkPublishEntry Entry { get; } = entry; /// /// Error message as to why the entry failed to publish. /// - public string ErrorMessage { get; } -} \ No newline at end of file + public string ErrorMessage { get; } = errorMessage; +} diff --git a/src/Dapr.Client/BulkStateItem.cs b/src/Dapr.Client/BulkStateItem.cs index f834654db..d3b423f42 100644 --- a/src/Dapr.Client/BulkStateItem.cs +++ b/src/Dapr.Client/BulkStateItem.cs @@ -16,74 +16,54 @@ namespace Dapr.Client; /// /// Represents a state object returned from a bulk get state operation. /// -public readonly struct BulkStateItem +/// The state key. +/// The value. +/// The ETag. +/// +/// Application code should not need to create instances of . +/// +public readonly struct BulkStateItem(string key, string value, string etag) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The value. - /// The ETag. - /// - /// Application code should not need to create instances of . - /// - public BulkStateItem(string key, string value, string etag) - { - this.Key = key; - this.Value = value; - this.ETag = etag; - } - /// /// Gets the state key. /// - public string Key { get; } + public string Key { get; } = key; /// /// Gets the value. /// - public string Value { get; } + public string Value { get; } = value; /// /// Get the ETag. /// - public string ETag { get; } + public string ETag { get; } = etag; } /// /// Represents a state object returned from a bulk get state operation where the value has /// been deserialized to the specified type. /// -public readonly struct BulkStateItem +/// The state key. +/// The typed value. +/// The ETag. +/// +/// Application code should not need to create instances of . +/// +public readonly struct BulkStateItem(string key, TValue value, string etag) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The typed value. - /// The ETag. - /// - /// Application code should not need to create instances of . - /// - public BulkStateItem(string key, TValue value, string etag) - { - this.Key = key; - this.Value = value; - this.ETag = etag; - } - /// /// Gets the state key. /// - public string Key { get; } + public string Key { get; } = key; /// /// Gets the deserialized value of the indicated type. /// - public TValue Value { get; } + public TValue Value { get; } = value; /// /// Get the ETag. /// - public string ETag { get; } -} \ No newline at end of file + public string ETag { get; } = etag; +} diff --git a/src/Dapr.Client/CloudEvent.cs b/src/Dapr.Client/CloudEvent.cs index f9329cd21..485a615d6 100644 --- a/src/Dapr.Client/CloudEvent.cs +++ b/src/Dapr.Client/CloudEvent.cs @@ -44,24 +44,16 @@ public class CloudEvent /// /// Represents a CloudEvent with typed data. /// -public class CloudEvent : CloudEvent +public class CloudEvent(TData data) : CloudEvent { - /// - /// Initialize a new instance of the class. - /// - public CloudEvent(TData data) - { - Data = data; - } - /// /// CloudEvent 'data' content. /// - public TData Data { get; } + public TData Data { get; } = data; /// /// Gets event data. /// [JsonPropertyName("datacontenttype")] public string DataContentType { get; } = Constants.ContentTypeApplicationJson; -} \ No newline at end of file +} diff --git a/src/Dapr.Client/ConfigurationItem.cs b/src/Dapr.Client/ConfigurationItem.cs index fb4cfef62..9478462b6 100644 --- a/src/Dapr.Client/ConfigurationItem.cs +++ b/src/Dapr.Client/ConfigurationItem.cs @@ -5,34 +5,24 @@ namespace Dapr.Client; /// /// Class that represents an item fetched from the Dapr Configuration API. /// -public class ConfigurationItem +/// The value of the configuration item. +/// The version of the fetched item. +/// The metadata associated with the request. +public class ConfigurationItem(string value, string version, IReadOnlyDictionary metadata) { - /// - /// Constructor for a ConfigurationItem. - /// - /// The value of the configuration item. - /// The version of the fetched item. - /// The metadata associated with the request. - public ConfigurationItem(string value, string version, IReadOnlyDictionary metadata) - { - Value = value; - Version = version; - Metadata = metadata; - } - /// /// The value of the configuration item. /// - public string Value { get; } + public string Value { get; } = value; /// /// The version of the item retrieved. This is only provided on responses and /// the statestore is not expected to keep all versions available. /// - public string Version { get; } + public string Version { get; } = version; /// /// The metadata that is passed to/from the statestore component. /// - public IReadOnlyDictionary Metadata { get; } -} \ No newline at end of file + public IReadOnlyDictionary Metadata { get; } = metadata; +} diff --git a/src/Dapr.Client/CryptographyOptions.cs b/src/Dapr.Client/CryptographyOptions.cs index 227753991..15f6ca7cf 100644 --- a/src/Dapr.Client/CryptographyOptions.cs +++ b/src/Dapr.Client/CryptographyOptions.cs @@ -6,21 +6,13 @@ namespace Dapr.Client; /// /// A collection of options used to configure how encryption cryptographic operations are performed. /// -public class EncryptionOptions +/// +public class EncryptionOptions(KeyWrapAlgorithm keyWrapAlgorithm) { - /// - /// Creates a new instance of the . - /// - /// - public EncryptionOptions(KeyWrapAlgorithm keyWrapAlgorithm) - { - KeyWrapAlgorithm = keyWrapAlgorithm; - } - /// /// The name of the algorithm used to wrap the encryption key. /// - public KeyWrapAlgorithm KeyWrapAlgorithm { get; set; } + public KeyWrapAlgorithm KeyWrapAlgorithm { get; set; } = keyWrapAlgorithm; private int streamingBlockSizeInBytes = 4 * 1024; // 4 KB /// @@ -34,11 +26,7 @@ public int StreamingBlockSizeInBytes get => streamingBlockSizeInBytes; set { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value); streamingBlockSizeInBytes = value; } } @@ -68,12 +56,8 @@ public int StreamingBlockSizeInBytes get => streamingBlockSizeInBytes; set { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value); streamingBlockSizeInBytes = value; } } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 98e96f595..96fe0cfde 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable using System; using System.Collections.Generic; using System.IO; @@ -76,7 +77,7 @@ public abstract class DaprClient : IDisposable /// a single client object can be reused for the life of the application. /// /// - public static HttpClient CreateInvokeHttpClient(string appId = null, string daprEndpoint = null, string daprApiToken = null) + public static HttpClient CreateInvokeHttpClient(string? appId = null, string? daprEndpoint = null, string? daprApiToken = null) { var handler = new InvocationHandler() { @@ -85,7 +86,7 @@ public static HttpClient CreateInvokeHttpClient(string appId = null, string dapr DefaultAppId = appId, }; - if (daprEndpoint is string) + if (daprEndpoint is not null) { // DaprEndpoint performs validation. handler.DaprEndpoint = daprEndpoint; @@ -94,7 +95,7 @@ public static HttpClient CreateInvokeHttpClient(string appId = null, string dapr var httpClient = new HttpClient(handler); httpClient.DefaultRequestHeaders.UserAgent.Add(UserAgent()); - if (appId is string) + if (appId is not null) { try { @@ -136,7 +137,7 @@ public static HttpClient CreateInvokeHttpClient(string appId = null, string dapr /// can be used throughout the lifetime of the application. /// /// - public static CallInvoker CreateInvocationInvoker(string appId, string daprEndpoint = null, string daprApiToken = null) + public static CallInvoker CreateInvocationInvoker(string appId, string? daprEndpoint = null, string? daprApiToken = null) { var channel = GrpcChannel.ForAddress(daprEndpoint ?? DaprDefaults.GetDefaultGrpcEndpoint()); return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null))); @@ -223,7 +224,7 @@ public abstract Task> BulkPublishEventAsync( string pubsubName, string topicName, IReadOnlyList events, - Dictionary metadata = default, + Dictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -241,7 +242,7 @@ public abstract Task PublishByteEventAsync( string topicName, ReadOnlyMemory data, string dataContentType = Constants.ContentTypeApplicationJson, - Dictionary metadata = default, + Dictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -258,7 +259,7 @@ public abstract Task InvokeBindingAsync( string bindingName, string operation, TRequest data, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -276,7 +277,7 @@ public abstract Task InvokeBindingAsync( string bindingName, string operation, TRequest data, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -444,8 +445,7 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri /// A that can be used to cancel the operation. /// 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 @@ -467,7 +467,6 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri /// /// public abstract HttpClient CreateInvokableHttpClient(string? appId = null); -#nullable disable /// /// Perform service invocation using the request provided by . If the response has a non-success @@ -551,7 +550,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(appId, methodName, data); + var request = CreateInvokeMethodRequest(appId, methodName, data); return InvokeMethodAsync(request, cancellationToken); } @@ -575,7 +574,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); return InvokeMethodAsync(request, cancellationToken); } @@ -643,7 +642,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(appId, methodName, data); + var request = CreateInvokeMethodRequest(appId, methodName, data); return InvokeMethodAsync(request, cancellationToken); } @@ -669,7 +668,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); return InvokeMethodAsync(request, cancellationToken); } @@ -753,7 +752,7 @@ public abstract Task InvokeMethodGrpcAsync( /// A that can be used to cancel the operation. /// The data type of the value to read. /// A that will return the value when the operation has completed. - public abstract Task GetStateAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + public abstract Task GetStateAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// /// Gets a list of values associated with the from the Dapr state store. @@ -764,7 +763,7 @@ public abstract Task InvokeMethodGrpcAsync( /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. /// A that can be used to cancel the operation. /// A that will return the list of values when the operation has completed. - public abstract Task> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + public abstract Task> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// /// Gets a list of deserialized values associated with the from the Dapr state store. This overload should be used @@ -778,7 +777,7 @@ public abstract Task InvokeMethodGrpcAsync( /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. /// A that can be used to cancel the operation. /// A that will return the list of deserialized values when the operation has completed. - public abstract Task>> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + public abstract Task>> GetBulkStateAsync(string storeName, IReadOnlyList keys, int? parallelism, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// /// Saves a list of to the Dapr state store. @@ -808,7 +807,7 @@ public abstract Task InvokeMethodGrpcAsync( /// A collection of metadata key-value pairs that will be provided to the state store. The valid metadata keys and values are determined by the type of state store used. /// A that can be used to cancel the operation. /// A that will return the value when the operation has completed. This wraps the read value and an ETag. - public abstract Task<(TValue value, string etag)> GetStateAndETagAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default); + public abstract Task<(TValue value, string etag)> GetStateAndETagAsync(string storeName, string key, ConsistencyMode? consistencyMode = null, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// /// Gets a for the current value associated with the from @@ -821,7 +820,7 @@ public abstract Task InvokeMethodGrpcAsync( /// A that can be used to cancel the operation. /// The type of the data that will be JSON deserialized from the state store response. /// A that will return the when the operation has completed. - public async Task> GetStateEntryAsync(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + public async Task> GetStateEntryAsync(string storeName, string key, ConsistencyMode? consistencyMode = null, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default) { ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); @@ -846,8 +845,8 @@ public abstract Task SaveStateAsync( string storeName, string key, TValue value, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, + StateOptions? stateOptions = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); @@ -866,8 +865,8 @@ public abstract Task SaveByteStateAsync( string storeName, string key, ReadOnlyMemory binaryValue, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, + StateOptions? stateOptions = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -887,8 +886,8 @@ public abstract Task TrySaveByteStateAsync( string key, ReadOnlyMemory binaryValue, string etag, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, + StateOptions? stateOptions = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); @@ -904,8 +903,8 @@ public abstract Task TrySaveByteStateAsync( public abstract Task> GetByteStateAsync( string storeName, string key, - ConsistencyMode? consistencyMode = default, - IReadOnlyDictionary metadata = default, + ConsistencyMode? consistencyMode = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -920,8 +919,8 @@ public abstract Task> GetByteStateAsync( public abstract Task<(ReadOnlyMemory, string etag)> GetByteStateAndETagAsync( string storeName, string key, - ConsistencyMode? consistencyMode = default, - IReadOnlyDictionary metadata = default, + ConsistencyMode? consistencyMode = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -943,8 +942,8 @@ public abstract Task TrySaveStateAsync( string key, TValue value, string etag, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, + StateOptions? stateOptions = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -959,7 +958,7 @@ public abstract Task TrySaveStateAsync( public abstract Task ExecuteStateTransactionAsync( string storeName, IReadOnlyList operations, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -974,8 +973,8 @@ public abstract Task ExecuteStateTransactionAsync( public abstract Task DeleteStateAsync( string storeName, string key, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, + StateOptions? stateOptions = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -993,8 +992,8 @@ public abstract Task TryDeleteStateAsync( string storeName, string key, string etag, - StateOptions stateOptions = default, - IReadOnlyDictionary metadata = default, + StateOptions? stateOptions = null, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -1010,7 +1009,7 @@ public abstract Task TryDeleteStateAsync( public abstract Task> QueryStateAsync( string storeName, string jsonQuery, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -1024,7 +1023,7 @@ public abstract Task> QueryStateAsync( public abstract Task> GetSecretAsync( string storeName, string key, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -1036,7 +1035,7 @@ public abstract Task> GetSecretAsync( /// A that will return the value when the operation has completed. public abstract Task>> GetBulkSecretAsync( string storeName, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -1050,7 +1049,7 @@ public abstract Task>> GetBulkSecr public abstract Task GetConfiguration( string storeName, IReadOnlyList keys, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -1064,7 +1063,7 @@ public abstract Task GetConfiguration( public abstract Task SubscribeConfiguration( string storeName, IReadOnlyList keys, - IReadOnlyDictionary metadata = default, + IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default); /// @@ -1416,12 +1415,11 @@ protected virtual void Dispose(bool disposing) /// A containing the value to use for User-Agent. protected static ProductInfoHeaderValue UserAgent() { - var assembly = typeof(DaprClient).Assembly; - string assemblyVersion = assembly + var assemblyVersion = typeof(DaprClient).Assembly .GetCustomAttributes() .FirstOrDefault()? .InformationalVersion; return new ProductInfoHeaderValue("dapr-sdk-dotnet", $"v{assemblyVersion}"); } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index 394b313e2..9ee860cfd 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -11,8 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using Dapr.Common.Extensions; - namespace Dapr.Client; using System; @@ -23,7 +21,6 @@ namespace Dapr.Client; using System.Net.Http; using System.Net.Http.Json; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -207,7 +204,7 @@ private async Task> MakeBulkPublishRequest( Dictionary> entryMap = new Dictionary>(); - for (int counter = 0; counter < events.Count; counter++) + for (var counter = 0; counter < events.Count; counter++) { var entry = new Autogenerated.BulkPublishRequestEntry() { @@ -217,7 +214,6 @@ private async Task> MakeBulkPublishRequest( events[counter] is CloudEvent ? Constants.ContentTypeCloudEvent : Constants.ContentTypeApplicationJson, - Metadata = { }, }; envelope.Entries.Add(entry); entryMap.Add(counter.ToString(), new BulkPublishEntry( @@ -274,7 +270,7 @@ public override async Task InvokeBindingAsync( ArgumentVerifier.ThrowIfNullOrEmpty(bindingName, nameof(bindingName)); ArgumentVerifier.ThrowIfNullOrEmpty(operation, nameof(operation)); - var bytes = TypeConverters.ToJsonByteString(data, this.jsonSerializerOptions); + var bytes = TypeConverters.ToJsonByteString(data, this.jsonSerializerOptions); _ = await MakeInvokeBindingRequestAsync(bindingName, operation, bytes, metadata, cancellationToken); } @@ -289,7 +285,7 @@ public override async Task InvokeBindingAsync( ArgumentVerifier.ThrowIfNullOrEmpty(bindingName, nameof(bindingName)); ArgumentVerifier.ThrowIfNullOrEmpty(operation, nameof(operation)); - var bytes = TypeConverters.ToJsonByteString(data, this.jsonSerializerOptions); + var bytes = TypeConverters.ToJsonByteString(data, this.jsonSerializerOptions); var response = await MakeInvokeBindingRequestAsync(bindingName, operation, bytes, metadata, cancellationToken); try @@ -423,7 +419,7 @@ public override HttpRequestMessage CreateInvokeMethodRequest(HttpMetho ArgumentVerifier.ThrowIfNull(methodName, nameof(methodName)); var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, queryStringParameters); - request.Content = JsonContent.Create(data, options: this.JsonSerializerOptions); + request.Content = JsonContent.Create(data, options: this.JsonSerializerOptions); return request; } @@ -432,7 +428,7 @@ public override async Task InvokeMethodWithResponseAsync(Ht { ArgumentVerifier.ThrowIfNull(request, nameof(request)); - if (!this.httpEndpoint.IsBaseOf(request.RequestUri)) + if (request.RequestUri != null && !this.httpEndpoint.IsBaseOf(request.RequestUri)) { throw new InvalidOperationException("The provided request URI is not a Dapr service invocation URI."); } @@ -451,8 +447,8 @@ public override async Task InvokeMethodWithResponseAsync(Ht request.Options.TryGetValue(new HttpRequestOptionsKey(MethodNameKey), out var methodName); throw new InvocationException( - appId: appId as string, - methodName: methodName as string, + appId: appId, + methodName: methodName, innerException: ex, response: null); } @@ -501,8 +497,8 @@ public async override Task InvokeMethodAsync(HttpRequestMessage request, request.Options.TryGetValue(new HttpRequestOptionsKey(MethodNameKey), out var methodName); throw new InvocationException( - appId: appId as string, - methodName: methodName as string, + appId: appId, + methodName: methodName, innerException: ex, response: response); } @@ -526,8 +522,8 @@ public async override Task InvokeMethodAsync(HttpRequestMe request.Options.TryGetValue(new HttpRequestOptionsKey(MethodNameKey), out var methodName); throw new InvocationException( - appId: appId as string, - methodName: methodName as string, + appId: appId, + methodName: methodName, innerException: ex, response: response); } @@ -544,8 +540,8 @@ public async override Task InvokeMethodAsync(HttpRequestMe request.Options.TryGetValue(new HttpRequestOptionsKey(MethodNameKey), out var methodName); throw new InvocationException( - appId: appId as string, - methodName: methodName as string, + appId: appId, + methodName: methodName, innerException: ex, response: response); } @@ -555,8 +551,8 @@ public async override Task InvokeMethodAsync(HttpRequestMe request.Options.TryGetValue(new HttpRequestOptionsKey(MethodNameKey), out var methodName); throw new InvocationException( - appId: appId as string, - methodName: methodName as string, + appId: appId, + methodName: methodName, innerException: ex, response: response); } @@ -1099,7 +1095,7 @@ public override async Task DeleteBulkStateAsync(string storeName, IReadOnlyList< public override async Task<(TValue value, string etag)> GetStateAndETagAsync( string storeName, string key, - ConsistencyMode? consistencyMode = default, + ConsistencyMode? consistencyMode = null, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) { @@ -1283,7 +1279,7 @@ private async Task MakeExecuteStateTransactionCallAsync( { var stateOperation = new Autogenerated.TransactionalStateOperation { - OperationType = state.OperationType.ToString().ToLower(), Request = ToAutogeneratedStateItem(state) + OperationType = state.OperationType.ToString()?.ToLower(), Request = ToAutogeneratedStateItem(state) }; envelope.Operations.Add(stateOperation); @@ -1579,7 +1575,7 @@ public async override Task GetConfiguration( var request = new Autogenerated.GetConfigurationRequest() { StoreName = storeName }; - if (keys != null && keys.Count > 0) + if (keys is { Count: > 0 }) { request.Keys.AddRange(keys); } @@ -1593,7 +1589,7 @@ public async override Task GetConfiguration( } var options = CreateCallOptions(headers: null, cancellationToken); - Autogenerated.GetConfigurationResponse response = new Autogenerated.GetConfigurationResponse(); + Autogenerated.GetConfigurationResponse response; try { response = await client.GetConfigurationAsync(request, options); @@ -1815,7 +1811,7 @@ public override async Task>> DecryptAsync( //At the same time, retrieve the decrypted response from the sidecar receiveResult) //Return only the result of the `RetrieveEncryptedStreamAsync` method - .ContinueWith(t => receiveResult.Result, cancellationToken); + .ContinueWith(_ => receiveResult.Result, cancellationToken); } /// @@ -2220,7 +2216,7 @@ public async override Task Unlock( }; var options = CreateCallOptions(headers: null, cancellationToken); - Autogenerated.UnlockResponse response = new Autogenerated.UnlockResponse(); + Autogenerated.UnlockResponse response; try { response = await client.UnlockAlpha1Async(request, options); @@ -2368,12 +2364,15 @@ private CallOptions CreateCallOptions(Metadata headers, CancellationToken cancel { var options = new CallOptions(headers: headers ?? new Metadata(), cancellationToken: cancellationToken); - options.Headers.Add("User-Agent", UserAgent().ToString()); - - // add token for dapr api token based authentication - if (this.apiTokenHeader is not null) + if (options.Headers != null) { - options.Headers.Add(this.apiTokenHeader.Value.Key, this.apiTokenHeader.Value.Value); + options.Headers.Add("User-Agent", UserAgent().ToString()); + + // add token for dapr api token based authentication + if (this.apiTokenHeader is not null) + { + options.Headers.Add(this.apiTokenHeader.Value.Key, this.apiTokenHeader.Value.Value); + } } return options; diff --git a/src/Dapr.Client/DaprMetadata.cs b/src/Dapr.Client/DaprMetadata.cs index e9e4f5bc8..2d12f1e84 100644 --- a/src/Dapr.Client/DaprMetadata.cs +++ b/src/Dapr.Client/DaprMetadata.cs @@ -18,108 +18,77 @@ namespace Dapr.Client; /// /// Represents a metadata object returned from dapr sidecar. /// -public sealed class DaprMetadata +/// The application id. +/// The registered actors metadata. +/// The list of custom attributes as key-value pairs, where key is the attribute name. +/// The loaded components metadata. +public sealed class DaprMetadata(string id, IReadOnlyList actors, IReadOnlyDictionary extended, IReadOnlyList components) { - /// - /// Initializes a new instance of the class. - /// - /// The application id. - /// The registered actors metadata. - /// The list of custom attributes as key-value pairs, where key is the attribute name. - /// The loaded components metadata. - public DaprMetadata(string id, IReadOnlyList actors, IReadOnlyDictionary extended, IReadOnlyList components) - { - Id = id; - Actors = actors; - Extended = extended; - Components = components; - } - /// /// Gets the application id. /// - public string Id { get; } + public string Id { get; } = id; /// /// Gets the registered actors metadata. /// - public IReadOnlyList Actors { get; } + public IReadOnlyList Actors { get; } = actors; /// /// Gets the list of custom attributes as key-value pairs, where key is the attribute name. /// - public IReadOnlyDictionary Extended { get; } + public IReadOnlyDictionary Extended { get; } = extended; /// /// Gets the loaded components metadata. /// - public IReadOnlyList Components { get; } + public IReadOnlyList Components { get; } = components; } /// /// Represents a actor metadata object returned from dapr sidecar. /// -public sealed class DaprActorMetadata +/// This registered actor type. +/// The number of actors running. +public sealed class DaprActorMetadata(string type, int count) { - /// - /// Initializes a new instance of the class. - /// - /// This registered actor type. - /// The number of actors running. - public DaprActorMetadata(string type, int count) - { - Type = type; - Count = count; - } - /// /// Gets the registered actor type. /// - public string Type { get; } + public string Type { get; } = type; /// /// Gets the number of actors running. /// - public int Count { get; } + public int Count { get; } = count; } /// /// Represents a components metadata object returned from dapr sidecar. /// -public sealed class DaprComponentsMetadata +/// The name of the component. +/// The component type. +/// The component version. +/// The supported capabilities for this component type and version. +public sealed class DaprComponentsMetadata(string name, string type, string version, string[] capabilities) { - /// - /// Initializes a new instance of the class. - /// - /// The name of the component. - /// The component type. - /// The component version. - /// The supported capabilities for this component type and version. - public DaprComponentsMetadata(string name, string type, string version, string[] capabilities) - { - Name = name; - Type = type; - Version = version; - Capabilities = capabilities; - } - /// /// Gets the name of the component. /// - public string Name { get; } + public string Name { get; } = name; /// /// Gets the component type. /// - public string Type { get; } + public string Type { get; } = type; /// /// Gets the component version. /// - public string Version { get; } + public string Version { get; } = version; /// /// Gets the supported capabilities for this component type and version. /// - public string[] Capabilities { get; } -} \ No newline at end of file + public string[] Capabilities { get; } = capabilities; +} diff --git a/src/Dapr.Client/DaprSubscribeConfigurationSource.cs b/src/Dapr.Client/DaprSubscribeConfigurationSource.cs index 98947767f..b5214c9a5 100644 --- a/src/Dapr.Client/DaprSubscribeConfigurationSource.cs +++ b/src/Dapr.Client/DaprSubscribeConfigurationSource.cs @@ -40,10 +40,10 @@ internal DaprSubscribeConfigurationSource(AsyncServerStreamingCall> GetAsyncEnumerator(CancellationToken cancellationToken = default) { - return new DaprSubscribeConfigurationEnumerator(call, streamId => setIdIfNullOrEmpty(streamId), cancellationToken); + return new DaprSubscribeConfigurationEnumerator(call, SetIdIfNullOrEmpty, cancellationToken); } - private void setIdIfNullOrEmpty(string id) + private void SetIdIfNullOrEmpty(string id) { if (string.IsNullOrEmpty(this.id)) { @@ -52,22 +52,8 @@ private void setIdIfNullOrEmpty(string id) } } -internal class DaprSubscribeConfigurationEnumerator : IAsyncEnumerator> +internal class DaprSubscribeConfigurationEnumerator(AsyncServerStreamingCall call, Action idCallback, CancellationToken cancellationToken = default) : IAsyncEnumerator> { - private AsyncServerStreamingCall call; - private Action idCallback; - private CancellationToken cancellationToken; - - internal DaprSubscribeConfigurationEnumerator( - AsyncServerStreamingCall call, - Action idCallback, - CancellationToken cancellationToken = default) - { - this.call = call; - this.idCallback = idCallback; - this.cancellationToken = cancellationToken; - } - /// public IDictionary Current { @@ -95,4 +81,4 @@ public async ValueTask MoveNextAsync() { return await call.ResponseStream.MoveNext(cancellationToken); } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/DeleteBulkStateItem.cs b/src/Dapr.Client/DeleteBulkStateItem.cs index b7eb6c0db..8f0059819 100644 --- a/src/Dapr.Client/DeleteBulkStateItem.cs +++ b/src/Dapr.Client/DeleteBulkStateItem.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable using System.Collections.Generic; namespace Dapr.Client; @@ -18,40 +19,29 @@ namespace Dapr.Client; /// /// Represents a state object used for bulk delete state operation /// -public readonly struct BulkDeleteStateItem +/// The state key. +/// The ETag. +/// The stateOptions. +/// The metadata. +public readonly struct BulkDeleteStateItem(string key, string etag, StateOptions? stateOptions = null, IReadOnlyDictionary? metadata = null) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The ETag. - /// The stateOptions. - /// The metadata. - public BulkDeleteStateItem(string key, string etag, StateOptions stateOptions = default, IReadOnlyDictionary metadata = default) - { - this.Key = key; - this.ETag = etag; - StateOptions = stateOptions; - Metadata = metadata; - } - /// /// Gets the state key. /// - public string Key { get; } + public string Key { get; } = key; /// /// Get the ETag. /// - public string ETag { get; } + public string ETag { get; } = etag; /// /// Gets the StateOptions. /// - public StateOptions StateOptions { get; } + public StateOptions? StateOptions { get; } = stateOptions; /// /// Gets the Metadata. /// - public IReadOnlyDictionary Metadata { get; } -} \ No newline at end of file + public IReadOnlyDictionary? Metadata { get; } = metadata; +} diff --git a/src/Dapr.Client/GetConfigurationResponse.cs b/src/Dapr.Client/GetConfigurationResponse.cs index 317a88514..c707345d7 100644 --- a/src/Dapr.Client/GetConfigurationResponse.cs +++ b/src/Dapr.Client/GetConfigurationResponse.cs @@ -18,27 +18,11 @@ namespace Dapr.Client; /// /// Class representing the response from a GetConfiguration API call. /// -public class GetConfigurationResponse +/// The map of keys to items that was returned in the GetConfiguration call. +public class GetConfigurationResponse(IReadOnlyDictionary configMap) { - private readonly IReadOnlyDictionary configMap; - - /// - /// Constructor for a GetConfigurationResponse. - /// - /// The map of keys to items that was returned in the GetConfiguration call. - public GetConfigurationResponse(IReadOnlyDictionary configMap) - { - this.configMap = configMap; - } - /// /// The map of key to items returned in a GetConfiguration call. /// - public IReadOnlyDictionary Items - { - get - { - return configMap; - } - } -} \ No newline at end of file + public IReadOnlyDictionary Items => configMap; +} diff --git a/src/Dapr.Client/InvocationHandler.cs b/src/Dapr.Client/InvocationHandler.cs index af554e3ca..34277b785 100644 --- a/src/Dapr.Client/InvocationHandler.cs +++ b/src/Dapr.Client/InvocationHandler.cs @@ -42,17 +42,8 @@ namespace Dapr.Client; /// public class InvocationHandler : DelegatingHandler { - private Uri parsedEndpoint; - private string? apiToken; - - /// - /// Initializes a new instance of . - /// - public InvocationHandler() - { - this.parsedEndpoint = new Uri(DaprDefaults.GetDefaultHttpEndpoint(), UriKind.Absolute); - this.apiToken = DaprDefaults.GetDefaultDaprApiToken(null); - } + private Uri parsedEndpoint = new(DaprDefaults.GetDefaultHttpEndpoint(), UriKind.Absolute); + private string? apiToken = DaprDefaults.GetDefaultDaprApiToken(null); /// /// Gets or the sets the URI of the Dapr HTTP endpoint used for service invocation. @@ -60,10 +51,7 @@ public InvocationHandler() /// The URI of the Dapr HTTP endpoint used for service invocation. public string DaprEndpoint { - get - { - return this.parsedEndpoint.OriginalString; - } + get => this.parsedEndpoint.OriginalString; set { if (value == null) @@ -106,11 +94,16 @@ protected override async Task SendAsync(HttpRequestMessage try { - var apiTokenHeader = DaprClient.GetDaprApiTokenHeader(this.apiToken); - if (apiTokenHeader is not null) + var token = this.apiToken; + if (token != null) { - request.Headers.Add(apiTokenHeader.Value.Key, apiTokenHeader.Value.Value); + var apiTokenHeader = DaprClient.GetDaprApiTokenHeader(token); + if (apiTokenHeader is not null) + { + request.Headers.Add(apiTokenHeader.Value.Key, apiTokenHeader.Value.Value); + } } + request.RequestUri = rewritten; return await base.SendAsync(request, cancellationToken); @@ -156,4 +149,4 @@ internal bool TryRewriteUri(Uri? uri, [NotNullWhen(true)] out Uri? rewritten) rewritten = builder.Uri; return true; } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/InvocationInterceptor.cs b/src/Dapr.Client/InvocationInterceptor.cs index 434e53ae8..0d297b65c 100644 --- a/src/Dapr.Client/InvocationInterceptor.cs +++ b/src/Dapr.Client/InvocationInterceptor.cs @@ -19,22 +19,10 @@ namespace Dapr.Client; /// /// gRPC interceptor which adds the required headers for Dapr gRPC proxying. /// -public class InvocationInterceptor : Interceptor +/// The Id of the Dapr Application. +/// The api token used for authentication, can be null. +public class InvocationInterceptor(string appId, string daprApiToken) : Interceptor { - private string appId; - private string daprApiToken; - - /// - /// Constructor. - /// The Id of the Dapr Application. - /// The api token used for authentication, can be null. - /// - public InvocationInterceptor(string appId, string daprApiToken) - { - this.appId = appId; - this.daprApiToken = daprApiToken; - } - /// /// Intercept and add headers to a BlockingUnaryCall. /// @@ -121,7 +109,7 @@ private void AddCallerMetadata(ref ClientInterceptorContext // Need to create a new context with headers for the call. if (headers == null) { - headers = new Metadata(); + headers = []; var options = context.Options.WithHeaders(headers); context = new ClientInterceptorContext(context.Method, context.Host, options); } @@ -133,4 +121,4 @@ private void AddCallerMetadata(ref ClientInterceptorContext headers.Add("dapr-api-token", daprApiToken); } } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/SaveStateItem.cs b/src/Dapr.Client/SaveStateItem.cs index 2e0da6839..4df89e20e 100644 --- a/src/Dapr.Client/SaveStateItem.cs +++ b/src/Dapr.Client/SaveStateItem.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable using System.Collections.Generic; namespace Dapr.Client; @@ -18,47 +19,35 @@ namespace Dapr.Client; /// /// Represents a state object used for bulk delete state operation /// -public readonly struct SaveStateItem +/// The state key. +/// The state value. +/// The ETag. +/// The stateOptions. +/// The metadata. +public readonly struct SaveStateItem(string key, TValue value, string etag, StateOptions? stateOptions = null, IReadOnlyDictionary? metadata = null) { - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The state value. - /// The ETag. - /// The stateOptions. - /// The metadata. - public SaveStateItem(string key, TValue value, string etag, StateOptions stateOptions = default, IReadOnlyDictionary metadata = default) - { - this.Key = key; - this.Value = value; - this.ETag = etag; - this.StateOptions = stateOptions; - this.Metadata = metadata; - } - /// /// Gets the state key. /// - public string Key { get; } - + public string Key { get; } = key; + /// /// Gets the state value. /// - public TValue Value { get; } + public TValue Value { get; } = value; /// /// Get the ETag. /// - public string ETag { get; } + public string ETag { get; } = etag; /// /// Gets the StateOptions. /// - public StateOptions StateOptions { get; } + public StateOptions? StateOptions { get; } = stateOptions; /// /// Gets the Metadata. /// - public IReadOnlyDictionary Metadata { get; } -} \ No newline at end of file + public IReadOnlyDictionary? Metadata { get; } = metadata; +} diff --git a/src/Dapr.Client/StateEntry.cs b/src/Dapr.Client/StateEntry.cs index 723051ffb..01c362faf 100644 --- a/src/Dapr.Client/StateEntry.cs +++ b/src/Dapr.Client/StateEntry.cs @@ -11,6 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable +using System; + namespace Dapr; using System.Collections.Generic; @@ -22,56 +25,37 @@ namespace Dapr; /// Represents a value in the Dapr state store. /// /// The data type of the value. -public sealed class StateEntry +/// The instance used to retrieve the value. +/// The name of the state store. +/// The state key. +/// The value. +/// The ETag. +/// +/// Application code should not need to create instances of . Use +/// to access +/// state entries. +/// +public sealed class StateEntry(DaprClient client, string storeName, string key, TValue value, string etag) { - private readonly DaprClient client; - - /// - /// Initializes a new instance of the class. - /// - /// The instance used to retrieve the value. - /// The name of the state store. - /// The state key. - /// The value. - /// The ETag. - /// - /// Application code should not need to create instances of . Use - /// to access - /// state entries. - /// - public StateEntry(DaprClient client, string storeName, string key, TValue value, string etag) - { - ArgumentVerifier.ThrowIfNull(client, nameof(client)); - ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName)); - ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key)); - - this.StoreName = storeName; - this.Key = key; - this.Value = value; - this.client = client; - - this.ETag = etag; - } - /// /// Gets the State Store Name. /// - public string StoreName { get; } + public string StoreName { get; } = storeName ?? throw new ArgumentNullException(nameof(storeName)); /// /// Gets the state key. /// - public string Key { get; } + public string Key { get; } = key ?? throw new ArgumentNullException(nameof(key)); /// /// Gets or sets the value locally. This is not sent to the state store until an API (e.g. is called. /// - public TValue Value { get; set; } + public TValue Value { get; set; } = value; /// /// The ETag. /// - public string ETag { get; } + public string ETag { get; } = etag; /// /// Deletes the entry associated with in the state store. @@ -80,10 +64,10 @@ public StateEntry(DaprClient client, string storeName, string key, TValue value, /// An key/value pair that may be consumed by the state store. This depends on the state store used. /// A that can be used to cancel the operation. /// A that will complete when the operation has completed. - public Task DeleteAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + public Task DeleteAsync(StateOptions? stateOptions = null, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default) { // ETag is intentionally not specified - return this.client.DeleteStateAsync(this.StoreName, this.Key, stateOptions, metadata, cancellationToken); + return client.DeleteStateAsync(this.StoreName, this.Key, stateOptions, metadata, cancellationToken); } /// @@ -93,10 +77,10 @@ public Task DeleteAsync(StateOptions stateOptions = default, IReadOnlyDictionary /// An key/value pair that may be consumed by the state store. This is dependent on the type of state store used. /// A that can be used to cancel the operation. /// A that will complete when the operation has completed. - public Task SaveAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + public Task SaveAsync(StateOptions? stateOptions = null, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default) { // ETag is intentionally not specified - return this.client.SaveStateAsync( + return client.SaveStateAsync( storeName: this.StoreName, key: this.Key, value: this.Value, @@ -112,9 +96,9 @@ public Task SaveAsync(StateOptions stateOptions = default, IReadOnlyDictionaryOptions for Save state operation. /// A that can be used to cancel the operation. /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. - public Task TrySaveAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + public Task TrySaveAsync(StateOptions? stateOptions = null, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default) { - return this.client.TrySaveStateAsync( + return client.TrySaveStateAsync( this.StoreName, this.Key, this.Value, @@ -132,9 +116,9 @@ public Task TrySaveAsync(StateOptions stateOptions = default, IReadOnlyDic /// An key/value pair that may be consumed by the state store. This depends on the state store used. /// A that can be used to cancel the operation. /// A that will complete when the operation has completed. If the wrapped value is true the operation suceeded. - public Task TryDeleteAsync(StateOptions stateOptions = default, IReadOnlyDictionary metadata = default, CancellationToken cancellationToken = default) + public Task TryDeleteAsync(StateOptions? stateOptions = null, IReadOnlyDictionary? metadata = null, CancellationToken cancellationToken = default) { - return this.client.TryDeleteStateAsync( + return client.TryDeleteStateAsync( this.StoreName, this.Key, this.ETag, @@ -142,4 +126,4 @@ public Task TryDeleteAsync(StateOptions stateOptions = default, IReadOnlyD metadata, cancellationToken); } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/StateQueryException.cs b/src/Dapr.Client/StateQueryException.cs index 6fd1dddec..867ec545f 100644 --- a/src/Dapr.Client/StateQueryException.cs +++ b/src/Dapr.Client/StateQueryException.cs @@ -6,28 +6,18 @@ namespace Dapr.Client; /// Exception that is thrown when an erorr is encountered during a call to the Query API. /// This exception contains the partial results (if any) from that exception. /// -public class StateQueryException : DaprApiException +/// The description of the exception from the source. +/// The response containing successful items, if any, in a response with errors. +/// The key(s) that encountered an error during the query. +public class StateQueryException(string message, StateQueryResponse response, IReadOnlyList failedKeys) : DaprApiException(message) { - /// - /// Constructor. - /// - /// The description of the exception from the source. - /// The response containing successful items, if any, in a response with errors. - /// The key(s) that encountered an error during the query. - public StateQueryException(string message, StateQueryResponse response, IReadOnlyList failedKeys) - : base(message) - { - Response = response; - FailedKeys = failedKeys; - } - /// /// The response containing successful items, if any, in a response with errors. /// - public StateQueryResponse Response { get; } + public StateQueryResponse Response { get; } = response; /// /// The key(s) that encountered an error during the query. /// - public IReadOnlyList FailedKeys { get; } -} \ No newline at end of file + public IReadOnlyList FailedKeys { get; } = failedKeys; +} diff --git a/src/Dapr.Client/StateQueryResponse.cs b/src/Dapr.Client/StateQueryResponse.cs index 7306a668c..b3aaca5cc 100644 --- a/src/Dapr.Client/StateQueryResponse.cs +++ b/src/Dapr.Client/StateQueryResponse.cs @@ -19,74 +19,53 @@ namespace Dapr.Client; /// /// Represents the response from a state query. /// -public class StateQueryResponse +/// The results of the query. +/// The pagination token to continue the query. +/// The metadata to be passed back to the caller. +public class StateQueryResponse(IReadOnlyList> results, string token, IReadOnlyDictionary metadata) { - /// - /// Constructor. - /// - /// The results of the query. - /// The pagination token to continue the query. - /// The metadata to be passed back to the caller. - public StateQueryResponse(IReadOnlyList> results, string token, IReadOnlyDictionary metadata) - { - Results = new List>(results); - Token = token; - Metadata = metadata; - } - /// /// The results of the query. /// - public IReadOnlyList> Results { get; } + public IReadOnlyList> Results { get; } = new List>(results); /// /// The pagination token to continue the query. /// - public string Token { get; } + public string Token { get; } = token; /// /// The metadata to be passed back to the caller. /// - public IReadOnlyDictionary Metadata { get; } + public IReadOnlyDictionary Metadata { get; } = metadata; } /// /// Represents an individual item from the results of a state query. /// -public class StateQueryItem +/// The key of the returned item. +/// The value of the returned item. +/// The ETag of the returned item. +/// The error, if one occurred, of the returned item. +public class StateQueryItem(string key, TValue data, string etag, string error) { - /// - /// Constructor. - /// - /// The key of the returned item. - /// The value of the returned item. - /// The ETag of the returned item. - /// The error, if one occurred, of the returned item. - public StateQueryItem(string key, TValue data, string etag, string error) - { - Key = key; - Data = data; - ETag = etag; - Error = error; - } - /// /// The key from the matched query. /// - public string Key { get; } + public string Key { get; } = key; /// /// The data of the key from the matched query. /// - public TValue? Data { get; } + public TValue? Data { get; } = data; /// /// The ETag for the key from the matched query. /// - public string ETag { get; } + public string ETag { get; } = etag; /// /// The error from the query, if one occurred. /// - public string Error { get; } -} \ No newline at end of file + public string Error { get; } = error; +} diff --git a/src/Dapr.Client/StateTransactionRequest.cs b/src/Dapr.Client/StateTransactionRequest.cs index a03a2bd6b..6d5f2c83f 100644 --- a/src/Dapr.Client/StateTransactionRequest.cs +++ b/src/Dapr.Client/StateTransactionRequest.cs @@ -11,6 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable +using System; + namespace Dapr.Client; using System.Collections.Generic; @@ -18,58 +21,41 @@ namespace Dapr.Client; /// /// Represents a single request in in a StateTransaction. /// -public sealed class StateTransactionRequest +/// The state key. +/// The serialized state value. +/// The operation type. +/// The etag (optional). +/// Additional key value pairs for the state (optional). +/// State options (optional). +public sealed class StateTransactionRequest(string key, byte[] value, StateOperationType operationType, string? etag = null, IReadOnlyDictionary? metadata = null, StateOptions? options = null) { - - /// - /// Initializes a new instance of the class. - /// - /// The state key. - /// The serialized state value. - /// The operation type. - /// The etag (optional). - /// Additional key value pairs for the state (optional). - /// State options (optional). - public StateTransactionRequest(string key, byte[] value, StateOperationType operationType, string etag = default, IReadOnlyDictionary metadata = default, StateOptions options = default) - { - ArgumentVerifier.ThrowIfNull(key, nameof(key)); - - this.Key = key; - this.Value = value; - this.OperationType = operationType; - this.ETag = etag; - this.Metadata = metadata; - this.Options = options; - } - - /// /// Gets the state key. /// - public string Key { get; } + public string Key { get; } = key ?? throw new ArgumentNullException(nameof(key)); /// /// Gets or sets the value locally. /// - public byte[] Value { get; set; } + public byte[] Value { get; set; } = value; /// /// The Operation type. /// - public StateOperationType OperationType { get; set; } + public StateOperationType? OperationType { get; set; } = operationType; /// /// The ETag (optional). /// - public string ETag { get; set; } + public string? ETag { get; set; } = etag; /// /// Additional key-value pairs to be passed to the state store (optional). /// - public IReadOnlyDictionary Metadata { get; set; } + public IReadOnlyDictionary? Metadata { get; set; } = metadata; /// /// State Options (optional). /// - public StateOptions Options; + public StateOptions? Options = options; } diff --git a/src/Dapr.Client/SubscribeConfigurationResponse.cs b/src/Dapr.Client/SubscribeConfigurationResponse.cs index 06969ad57..32694891f 100644 --- a/src/Dapr.Client/SubscribeConfigurationResponse.cs +++ b/src/Dapr.Client/SubscribeConfigurationResponse.cs @@ -5,33 +5,17 @@ namespace Dapr.Client; /// /// Response for a Subscribe Configuration request. /// -public class SubscribeConfigurationResponse +/// The that provides the actual data from the subscription. +public class SubscribeConfigurationResponse(ConfigurationSource source) { - private ConfigurationSource source; - - /// - /// Constructor. - /// - /// The that provides the actual data from the subscription. - public SubscribeConfigurationResponse(ConfigurationSource source) : base() - { - this.source = source; - } - /// /// The Id of the Subscription. This will be until the first result has been streamed. /// After that time, the Id can be used to unsubscribe. /// - public string Id - { - get - { - return source.Id; - } - } + public string Id => source.Id; /// /// Get the that is used to read the actual subscribed configuration data. /// public IAsyncEnumerable> Source => source; -} \ No newline at end of file +} diff --git a/src/Dapr.Client/TryLockResponse.cs b/src/Dapr.Client/TryLockResponse.cs index 251777d92..086eafc19 100644 --- a/src/Dapr.Client/TryLockResponse.cs +++ b/src/Dapr.Client/TryLockResponse.cs @@ -50,11 +50,11 @@ public TryLockResponse() } /// - public async ValueTask DisposeAsync() { - using (var client = new DaprClientBuilder().Build()) { - if(this.Success) { - await client.Unlock(StoreName, ResourceId, LockOwner); - } + public async ValueTask DisposeAsync() + { + using var client = new DaprClientBuilder().Build(); + if(this.Success) { + await client.Unlock(StoreName, ResourceId, LockOwner); } } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/TypeConverters.cs b/src/Dapr.Client/TypeConverters.cs index e1cb4e26d..a9f50b469 100644 --- a/src/Dapr.Client/TypeConverters.cs +++ b/src/Dapr.Client/TypeConverters.cs @@ -48,16 +48,11 @@ public static Any ToJsonAny(T data, JsonSerializerOptions options) public static T FromJsonByteString(ByteString bytes, JsonSerializerOptions options) { - if (bytes.Length == 0) - { - return default; - } - - return JsonSerializer.Deserialize(bytes.Span, options); + return bytes.Length == 0 ? default : JsonSerializer.Deserialize(bytes.Span, options); } public static T FromJsonAny(Any any, JsonSerializerOptions options) { return FromJsonByteString(any.Value, options); } -} \ No newline at end of file +} diff --git a/src/Dapr.Client/UnsubscribeConfigurationResponse.cs b/src/Dapr.Client/UnsubscribeConfigurationResponse.cs index 112578067..aaf651e9a 100644 --- a/src/Dapr.Client/UnsubscribeConfigurationResponse.cs +++ b/src/Dapr.Client/UnsubscribeConfigurationResponse.cs @@ -3,26 +3,17 @@ /// /// Response from an Unsubscribe Configuration call. /// -public class UnsubscribeConfigurationResponse +/// Boolean indicating success. +/// Message from the Configuration API. +public class UnsubscribeConfigurationResponse(bool ok, string message) { /// /// Boolean representing if the request was successful or not. /// - public bool Ok { get; } + public bool Ok { get; } = ok; /// /// The message from the Configuration API. /// - public string Message { get; } - - /// - /// Constructor. - /// - /// Boolean indicating success. - /// Message from the Configuration API. - public UnsubscribeConfigurationResponse(bool ok, string message) - { - Ok = ok; - Message = message; - } -} \ No newline at end of file + public string Message { get; } = message; +} From 89df477b5395363d10d4b5019748b153ae22ca02 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 08:56:58 -0500 Subject: [PATCH 09/12] Fixed break in example caused by nullability annotation change Signed-off-by: Whit Waldo --- src/Dapr.Client/StateTransactionRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Client/StateTransactionRequest.cs b/src/Dapr.Client/StateTransactionRequest.cs index 6d5f2c83f..150362c65 100644 --- a/src/Dapr.Client/StateTransactionRequest.cs +++ b/src/Dapr.Client/StateTransactionRequest.cs @@ -27,7 +27,7 @@ namespace Dapr.Client; /// The etag (optional). /// Additional key value pairs for the state (optional). /// State options (optional). -public sealed class StateTransactionRequest(string key, byte[] value, StateOperationType operationType, string? etag = null, IReadOnlyDictionary? metadata = null, StateOptions? options = null) +public sealed class StateTransactionRequest(string key, byte[]? value, StateOperationType operationType, string? etag = null, IReadOnlyDictionary? metadata = null, StateOptions? options = null) { /// /// Gets the state key. @@ -37,7 +37,7 @@ public sealed class StateTransactionRequest(string key, byte[] value, StateOpera /// /// Gets or sets the value locally. /// - public byte[] Value { get; set; } = value; + public byte[]? Value { get; set; } = value; /// /// The Operation type. From c23a1e042d042bca10c05a241e9eae70dcd88c9c Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 08:57:57 -0500 Subject: [PATCH 10/12] Refactored where streaming subscription example is located in solution to align with packages Signed-off-by: Whit Waldo --- all.sln | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/all.sln b/all.sln index 9a163b1d9..d4e50b8fa 100644 --- a/all.sln +++ b/all.sln @@ -155,6 +155,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JobsSample", "examples\Jobs EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Workflow.Test", "test\Dapr.Workflow.Test\Dapr.Workflow.Test.csproj", "{E90114C6-86FC-43B8-AE5C-D9273CF21FE4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Messaging", "Messaging", "{8DB002D2-19E9-4342-A86B-025A367DF3D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -471,12 +473,13 @@ Global {00359961-0C50-4BB1-A794-8B06DE991639} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9} {4E04EB35-7FD2-4FDB-B09A-F75CE24053B9} = {DD020B34-460F-455F-8D17-CF4A949F100B} {0EAE36A1-B578-4F13-A113-7A477ECA1BDA} = {27C5D71D-0721-4221-9286-B94AB07B58CF} - {290D1278-F613-4DF3-9DF5-F37E38CDC363} = {0EF6EA64-D7C3-420D-9890-EAE8D54A57E6} {C8BB6A85-A7EA-40C0-893D-F36F317829B3} = {27C5D71D-0721-4221-9286-B94AB07B58CF} {BF9828E9-5597-4D42-AA6E-6E6C12214204} = {DD020B34-460F-455F-8D17-CF4A949F100B} {D9697361-232F-465D-A136-4561E0E88488} = {D687DDC4-66C5-4667-9E3A-FD8B78ECAA78} {9CAF360E-5AD3-4C4F-89A0-327EEB70D673} = {D9697361-232F-465D-A136-4561E0E88488} {E90114C6-86FC-43B8-AE5C-D9273CF21FE4} = {DD020B34-460F-455F-8D17-CF4A949F100B} + {8DB002D2-19E9-4342-A86B-025A367DF3D1} = {D687DDC4-66C5-4667-9E3A-FD8B78ECAA78} + {290D1278-F613-4DF3-9DF5-F37E38CDC363} = {8DB002D2-19E9-4342-A86B-025A367DF3D1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} From 13055cefb4656785f86bc44b4e39b620cd7d579f Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 09:17:51 -0500 Subject: [PATCH 11/12] Refactored actor example to modernize Signed-off-by: Whit Waldo --- all.sln | 2 +- examples/Actor/ActorClient/ActorClient.csproj | 2 +- examples/Actor/ActorClient/Program.cs | 242 ++++++++---------- .../DemoActor.Interfaces.csproj} | 5 + .../IBankActor.cs | 21 +- .../IDemoActor.cs | 31 +-- examples/Actor/DemoActor/BankService.cs | 6 +- examples/Actor/DemoActor/DemoActor.cs | 56 ++-- examples/Actor/DemoActor/DemoActor.csproj | 3 +- examples/Actor/DemoActor/Program.cs | 29 ++- examples/Actor/DemoActor/Startup.cs | 58 ----- 11 files changed, 169 insertions(+), 286 deletions(-) rename examples/Actor/{IDemoActor/IDemoActor.csproj => DemoActor.Interfaces/DemoActor.Interfaces.csproj} (57%) rename examples/Actor/{IDemoActor => DemoActor.Interfaces}/IBankActor.cs (70%) rename examples/Actor/{IDemoActor => DemoActor.Interfaces}/IDemoActor.cs (84%) delete mode 100644 examples/Actor/DemoActor/Startup.cs diff --git a/all.sln b/all.sln index d4e50b8fa..eabc532e9 100644 --- a/all.sln +++ b/all.sln @@ -54,7 +54,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControllerSample", "example EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Actor", "Actor", "{02374BD0-BF0B-40F8-A04A-C4C4D61D4992}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IDemoActor", "examples\Actor\IDemoActor\IDemoActor.csproj", "{7957E852-1291-4FAA-9034-FB66CE817FF1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoActor.Interfaces", "examples\Actor\DemoActor.Interfaces\DemoActor.Interfaces.csproj", "{7957E852-1291-4FAA-9034-FB66CE817FF1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoActor", "examples\Actor\DemoActor\DemoActor.csproj", "{626D74DD-4F37-4F74-87A3-5A6888684F5E}" EndProject diff --git a/examples/Actor/ActorClient/ActorClient.csproj b/examples/Actor/ActorClient/ActorClient.csproj index 43aa659b1..9631e5175 100644 --- a/examples/Actor/ActorClient/ActorClient.csproj +++ b/examples/Actor/ActorClient/ActorClient.csproj @@ -6,7 +6,7 @@ - + diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs index af209dc1d..b578d488a 100644 --- a/examples/Actor/ActorClient/Program.cs +++ b/examples/Actor/ActorClient/Program.cs @@ -11,145 +11,123 @@ // limitations under the License. // ------------------------------------------------------------------------ -using Dapr.Actors.Communication; -using IDemoActor; - -namespace ActorClient; - using System; using System.Threading; using System.Threading.Tasks; using Dapr.Actors; using Dapr.Actors.Client; +using IDemoActor; + +var data = new MyData("ValueA", "ValueB"); + +// Create an actor Id. +var actorId = new ActorId("abc"); + +// Make strongly typed Actor calls with Remoting. +// DemoActor is the type registered with Dapr runtime in the service. +var proxy = ActorProxy.Create(actorId, "DemoActor"); + +Console.WriteLine("Making call using actor proxy to save data."); +await proxy.SaveData(data, TimeSpan.FromMinutes(10)); +Console.WriteLine("Making call using actor proxy to get data."); +var receivedData = await proxy.GetData(); +Console.WriteLine($"Received data is {receivedData}."); + +// Making some more calls to test methods. +try +{ + Console.WriteLine("Making calls to an actor method which has no argument and no return type."); + await proxy.TestNoArgumentNoReturnType(); +} +catch (Exception ex) +{ + Console.WriteLine($"ERROR: Got exception while making call to method with No Argument & No Return Type. Exception: {ex}"); +} -/// -/// Actor Client class. -/// -public class Program +try +{ + await proxy.TestThrowException(); +} +catch (ActorMethodInvocationException ex) { - /// - /// Entry point. - /// - /// Arguments. - /// A representing the asynchronous operation. - public static async Task Main(string[] args) + if (ex.InnerException is ActorInvokeException invokeEx && invokeEx.ActualExceptionType is "System.NotImplementedException") { - var data = new MyData() - { - PropertyA = "ValueA", - PropertyB = "ValueB", - }; - - // Create an actor Id. - var actorId = new ActorId("abc"); - - // Make strongly typed Actor calls with Remoting. - // DemoActor is the type registered with Dapr runtime in the service. - var proxy = ActorProxy.Create(actorId, "DemoActor"); - - Console.WriteLine("Making call using actor proxy to save data."); - await proxy.SaveData(data, TimeSpan.FromMinutes(10)); - Console.WriteLine("Making call using actor proxy to get data."); - var receivedData = await proxy.GetData(); - Console.WriteLine($"Received data is {receivedData}."); - - // Making some more calls to test methods. - try - { - Console.WriteLine("Making calls to an actor method which has no argument and no return type."); - await proxy.TestNoArgumentNoReturnType(); - } - catch (Exception ex) - { - Console.WriteLine($"ERROR: Got exception while making call to method with No Argument & No Return Type. Exception: {ex}"); - } - - try - { - await proxy.TestThrowException(); - } - catch (ActorMethodInvocationException ex) - { - if (ex.InnerException is ActorInvokeException invokeEx && invokeEx.ActualExceptionType is "System.NotImplementedException") - { - Console.WriteLine($"Got Correct Exception from actor method invocation."); - } - else - { - Console.WriteLine($"Got Incorrect Exception from actor method invocation. Exception {ex.InnerException}"); - } - } - - // Making calls without Remoting, this shows method invocation using InvokeMethodAsync methods, the method name and its payload is provided as arguments to InvokeMethodAsync methods. - Console.WriteLine("Making calls without Remoting."); - var nonRemotingProxy = ActorProxy.Create(actorId, "DemoActor"); - await nonRemotingProxy.InvokeMethodAsync("TestNoArgumentNoReturnType"); - await nonRemotingProxy.InvokeMethodAsync("SaveData", data); - await nonRemotingProxy.InvokeMethodAsync("GetData"); - - Console.WriteLine("Registering the timer and reminder"); - await proxy.RegisterTimer(); - await proxy.RegisterReminder(); - Console.WriteLine("Waiting so the timer and reminder can be triggered"); - await Task.Delay(6000); - - Console.WriteLine("Making call using actor proxy to get data after timer and reminder triggered"); - receivedData = await proxy.GetData(); - Console.WriteLine($"Received data is {receivedData}."); - - Console.WriteLine("Getting details of the registered reminder"); - var reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder}."); - - Console.WriteLine("Deregistering timer. Timers would any way stop if the actor is deactivated as part of Dapr garbage collection."); - await proxy.UnregisterTimer(); - Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); - await proxy.UnregisterReminder(); + Console.WriteLine($"Got Correct Exception from actor method invocation."); + } + else + { + Console.WriteLine($"Got Incorrect Exception from actor method invocation. Exception {ex.InnerException}"); + } +} + +// Making calls without Remoting, this shows method invocation using InvokeMethodAsync methods, the method name and its payload is provided as arguments to InvokeMethodAsync methods. +Console.WriteLine("Making calls without Remoting."); +var nonRemotingProxy = ActorProxy.Create(actorId, "DemoActor"); +await nonRemotingProxy.InvokeMethodAsync("TestNoArgumentNoReturnType"); +await nonRemotingProxy.InvokeMethodAsync("SaveData", data); +await nonRemotingProxy.InvokeMethodAsync("GetData"); + +Console.WriteLine("Registering the timer and reminder"); +await proxy.RegisterTimer(); +await proxy.RegisterReminder(); +Console.WriteLine("Waiting so the timer and reminder can be triggered"); +await Task.Delay(6000); + +Console.WriteLine("Making call using actor proxy to get data after timer and reminder triggered"); +receivedData = await proxy.GetData(); +Console.WriteLine($"Received data is {receivedData}."); + +Console.WriteLine("Getting details of the registered reminder"); +var reminder = await proxy.GetReminder(); +Console.WriteLine($"Received reminder is {reminder}."); + +Console.WriteLine("Deregistering timer. Timers would any way stop if the actor is deactivated as part of Dapr garbage collection."); +await proxy.UnregisterTimer(); +Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); +await proxy.UnregisterReminder(); - Console.WriteLine("Registering reminder with repetitions - The reminder will repeat 3 times."); - await proxy.RegisterReminderWithRepetitions(3); - Console.WriteLine("Waiting so the reminder can be triggered"); - await Task.Delay(5000); - Console.WriteLine("Getting details of the registered reminder"); - reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder?.ToString() ?? "None"} (expecting None)."); - Console.WriteLine("Registering reminder with ttl and repetitions, i.e. reminder stops when either condition is met - The reminder will repeat 2 times."); - await proxy.RegisterReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), 2); - Console.WriteLine("Getting details of the registered reminder"); - reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder}."); - Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); - await proxy.UnregisterReminder(); - - Console.WriteLine("Registering reminder and Timer with TTL - The reminder will self delete after 10 seconds."); - await proxy.RegisterReminderWithTtl(TimeSpan.FromSeconds(10)); - await proxy.RegisterTimerWithTtl(TimeSpan.FromSeconds(10)); - Console.WriteLine("Getting details of the registered reminder"); - reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder}."); - - // Track the reminder. - var timer = new Timer(async state => Console.WriteLine($"Received data: {await proxy.GetData()}"), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); - await Task.Delay(TimeSpan.FromSeconds(21)); - await timer.DisposeAsync(); - - Console.WriteLine("Creating a Bank Actor"); - var bank = ActorProxy.Create(ActorId.CreateRandom(), "DemoActor"); - while (true) - { - var balance = await bank.GetAccountBalance(); - Console.WriteLine($"Balance for account '{balance.AccountId}' is '{balance.Balance:c}'."); - - Console.WriteLine($"Withdrawing '{10m:c}'..."); - try - { - await bank.Withdraw(new WithdrawRequest() { Amount = 10m, }); - } - catch (ActorMethodInvocationException ex) - { - Console.WriteLine("Overdraft: " + ex.Message); - break; - } - } +Console.WriteLine("Registering reminder with repetitions - The reminder will repeat 3 times."); +await proxy.RegisterReminderWithRepetitions(3); +Console.WriteLine("Waiting so the reminder can be triggered"); +await Task.Delay(5000); +Console.WriteLine("Getting details of the registered reminder"); +reminder = await proxy.GetReminder(); +Console.WriteLine($"Received reminder is {reminder?.ToString() ?? "None"} (expecting None)."); +Console.WriteLine("Registering reminder with ttl and repetitions, i.e. reminder stops when either condition is met - The reminder will repeat 2 times."); +await proxy.RegisterReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), 2); +Console.WriteLine("Getting details of the registered reminder"); +reminder = await proxy.GetReminder(); +Console.WriteLine($"Received reminder is {reminder}."); +Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); +await proxy.UnregisterReminder(); + +Console.WriteLine("Registering reminder and Timer with TTL - The reminder will self delete after 10 seconds."); +await proxy.RegisterReminderWithTtl(TimeSpan.FromSeconds(10)); +await proxy.RegisterTimerWithTtl(TimeSpan.FromSeconds(10)); +Console.WriteLine("Getting details of the registered reminder"); +reminder = await proxy.GetReminder(); +Console.WriteLine($"Received reminder is {reminder}."); + +// Track the reminder. +var timer = new Timer(async state => Console.WriteLine($"Received data: {await proxy.GetData()}"), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); +await Task.Delay(TimeSpan.FromSeconds(21)); +await timer.DisposeAsync(); + +Console.WriteLine("Creating a Bank Actor"); +var bank = ActorProxy.Create(ActorId.CreateRandom(), "DemoActor"); +while (true) +{ + var balance = await bank.GetAccountBalance(); + Console.WriteLine($"Balance for account '{balance.AccountId}' is '{balance.Balance:c}'."); + + Console.WriteLine($"Withdrawing '{10m:c}'..."); + try + { + await bank.Withdraw(new WithdrawRequest(10m)); + } + catch (ActorMethodInvocationException ex) + { + Console.WriteLine($"Overdraft: {ex.Message}"); + break; } -} \ No newline at end of file +} diff --git a/examples/Actor/IDemoActor/IDemoActor.csproj b/examples/Actor/DemoActor.Interfaces/DemoActor.Interfaces.csproj similarity index 57% rename from examples/Actor/IDemoActor/IDemoActor.csproj rename to examples/Actor/DemoActor.Interfaces/DemoActor.Interfaces.csproj index 8b8a84b21..f4955942b 100644 --- a/examples/Actor/IDemoActor/IDemoActor.csproj +++ b/examples/Actor/DemoActor.Interfaces/DemoActor.Interfaces.csproj @@ -1,5 +1,10 @@  + + IDemoActor + enable + + diff --git a/examples/Actor/IDemoActor/IBankActor.cs b/examples/Actor/DemoActor.Interfaces/IBankActor.cs similarity index 70% rename from examples/Actor/IDemoActor/IBankActor.cs rename to examples/Actor/DemoActor.Interfaces/IBankActor.cs index 28242c522..92d847421 100644 --- a/examples/Actor/IDemoActor/IBankActor.cs +++ b/examples/Actor/DemoActor.Interfaces/IBankActor.cs @@ -24,22 +24,9 @@ public interface IBankActor : IActor Task Withdraw(WithdrawRequest withdraw); } -public class AccountBalance -{ - public string AccountId { get; set; } - - public decimal Balance { get; set; } -} +public sealed record AccountBalance(string AccountId, decimal Balance); -public class WithdrawRequest -{ - public decimal Amount { get; set; } -} +public sealed record WithdrawRequest(decimal Amount); -public class OverdraftException : Exception -{ - public OverdraftException(decimal balance, decimal amount) - : base($"Your current balance is {balance:c} - that's not enough to withdraw {amount:c}.") - { - } -} \ No newline at end of file +public class OverdraftException(decimal balance, decimal amount) + : Exception($"Your current balance is {balance:c} - that's not enough to withdraw {amount:c}."); diff --git a/examples/Actor/IDemoActor/IDemoActor.cs b/examples/Actor/DemoActor.Interfaces/IDemoActor.cs similarity index 84% rename from examples/Actor/IDemoActor/IDemoActor.cs rename to examples/Actor/DemoActor.Interfaces/IDemoActor.cs index ce8ab7855..c5fefdb9f 100644 --- a/examples/Actor/IDemoActor/IDemoActor.cs +++ b/examples/Actor/DemoActor.Interfaces/IDemoActor.cs @@ -98,9 +98,8 @@ public interface IDemoActor : IActor /// /// Gets the registered reminder. /// - /// The name of the reminder. /// A task that returns the reminder after completion. - Task GetReminder(); + Task GetReminder(); /// /// Unregisters the registered timer. @@ -112,37 +111,19 @@ public interface IDemoActor : IActor /// /// Data Used by the Sample Actor. /// -public class MyData +public sealed record MyData(string? PropertyA, string? PropertyB) { - /// - /// Gets or sets the value for PropertyA. - /// - public string PropertyA { get; set; } - - /// - /// Gets or sets the value for PropertyB. - /// - public string PropertyB { get; set; } - /// - public override string ToString() - { - var propAValue = this.PropertyA ?? "null"; - var propBValue = this.PropertyB ?? "null"; - return $"PropertyA: {propAValue}, PropertyB: {propBValue}"; - } + public override string ToString() => $"PropertyA: {PropertyA ?? "null"}, PropertyB: {PropertyB ?? "null"}"; } public class ActorReminderData { - public string Name { get; set; } + public string? Name { get; set; } public TimeSpan DueTime { get; set; } public TimeSpan Period { get; set; } - public override string ToString() - { - return $"Name: {this.Name}, DueTime: {this.DueTime}, Period: {this.Period}"; - } -} \ No newline at end of file + public override string ToString() => $"Name: {this.Name}, DueTime: {this.DueTime}, Period: {this.Period}"; +} diff --git a/examples/Actor/DemoActor/BankService.cs b/examples/Actor/DemoActor/BankService.cs index a88cd8da9..22d2ac44f 100644 --- a/examples/Actor/DemoActor/BankService.cs +++ b/examples/Actor/DemoActor/BankService.cs @@ -15,10 +15,10 @@ namespace DemoActor; -public class BankService +public sealed class BankService { // Allow overdraft of up to 50 (of whatever currency). - private readonly decimal OverdraftThreshold = -50m; + private const decimal OverdraftThreshold = -50m; public decimal Withdraw(decimal balance, decimal amount) { @@ -32,4 +32,4 @@ public decimal Withdraw(decimal balance, decimal amount) return updated; } -} \ No newline at end of file +} diff --git a/examples/Actor/DemoActor/DemoActor.cs b/examples/Actor/DemoActor/DemoActor.cs index 38defd672..3e5de369d 100644 --- a/examples/Actor/DemoActor/DemoActor.cs +++ b/examples/Actor/DemoActor/DemoActor.cs @@ -22,31 +22,21 @@ namespace DemoActor; // The following example showcases a few features of Actors // // Every actor should inherit from the Actor type, and must implement one or more actor interfaces. -// In this case the actor interfaces are IDemoActor and IBankActor. +// In this case the actor interfaces are DemoActor.Interfaces and IBankActor. // // For Actors to use Reminders, it must derive from IRemindable. // If you don't intend to use Reminder feature, you can skip implementing IRemindable and reminder // specific methods which are shown in the code below. -public class DemoActor : Actor, IDemoActor.IDemoActor, IBankActor, IRemindable +public class DemoActor(ActorHost host, BankService bank) : Actor(host), IDemoActor.IDemoActor, IBankActor, IRemindable { private const string StateName = "my_data"; - private readonly BankService bank; - - public DemoActor(ActorHost host, BankService bank) - : base(host) - { - // BankService is provided by dependency injection. - // See Program.cs - this.bank = bank; - } - public async Task SaveData(MyData data, TimeSpan ttl) { Console.WriteLine($"This is Actor id {this.Id} with data {data}."); // Set State using StateManager, state is saved after the method execution. - await this.StateManager.SetStateAsync(StateName, data, ttl); + await this.StateManager.SetStateAsync(StateName, data, ttl); } public Task GetData() @@ -85,7 +75,7 @@ public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repeti await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl); } - public async Task GetReminder() + public async Task GetReminder() { var reminder = await this.GetReminderAsync("TestReminder"); @@ -108,14 +98,17 @@ public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSp { // This method is invoked when an actor reminder is fired. var actorState = await this.StateManager.GetStateAsync(StateName); - actorState.PropertyB = $"Reminder triggered at '{DateTime.Now:yyyy-MM-ddTHH:mm:ss}'"; - await this.StateManager.SetStateAsync(StateName, actorState, ttl: TimeSpan.FromMinutes(5)); + var updatedActorState = actorState with + { + PropertyB = $"Reminder triggered at '{DateTime.Now:yyyy-MM-ddTHH:mm:ss}'" + }; + await this.StateManager.SetStateAsync(StateName, updatedActorState, ttl: TimeSpan.FromMinutes(5)); } class TimerParams { public int IntParam { get; set; } - public string StringParam { get; set; } + public string? StringParam { get; set; } } /// @@ -172,20 +165,19 @@ protected override Task OnDeactivateAsync() public async Task TimerCallback(byte[] data) { var state = await this.StateManager.GetStateAsync(StateName); - state.PropertyA = $"Timer triggered at '{DateTime.Now:yyyyy-MM-ddTHH:mm:s}'"; - await this.StateManager.SetStateAsync(StateName, state, ttl: TimeSpan.FromMinutes(5)); + var updatedState = state with { PropertyA = $"Timer triggered at '{DateTime.Now:yyyyy-MM-ddTHH:mm:s}'" }; + await this.StateManager.SetStateAsync(StateName, updatedState, ttl: TimeSpan.FromMinutes(5)); var timerParams = JsonSerializer.Deserialize(data); - Console.WriteLine("Timer parameter1: " + timerParams.IntParam); - Console.WriteLine("Timer parameter2: " + timerParams.StringParam); + if (timerParams != null) + { + Console.WriteLine($"Timer parameter1: {timerParams.IntParam}"); + Console.WriteLine($"Timer parameter2: {timerParams.StringParam ?? ""}"); + } } public async Task GetAccountBalance() { - var starting = new AccountBalance() - { - AccountId = this.Id.GetId(), - Balance = 100m, // Start new accounts with 100, we're pretty generous. - }; + var starting = new AccountBalance(this.Id.GetId(), 100m); // Start new accounts with 100 million; we're pretty generous var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); return balance; @@ -193,18 +185,14 @@ public async Task GetAccountBalance() public async Task Withdraw(WithdrawRequest withdraw) { - var starting = new AccountBalance() - { - AccountId = this.Id.GetId(), - Balance = 100m, // Start new accounts with 100, we're pretty generous. - }; + var starting = new AccountBalance(this.Id.GetId(), 100m); // Start new accounts with 100 million; we're pretty generous. var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); // Throws Overdraft exception if the account doesn't have enough money. - var updated = this.bank.Withdraw(balance.Balance, withdraw.Amount); + var updated = bank.Withdraw(balance.Balance, withdraw.Amount); - balance.Balance = updated; + balance = balance with { Balance = updated }; await this.StateManager.SetStateAsync("balance", balance); } -} \ No newline at end of file +} diff --git a/examples/Actor/DemoActor/DemoActor.csproj b/examples/Actor/DemoActor/DemoActor.csproj index 4ed29d66e..f6bf9e8ee 100644 --- a/examples/Actor/DemoActor/DemoActor.csproj +++ b/examples/Actor/DemoActor/DemoActor.csproj @@ -4,6 +4,7 @@ true true demo-actor + enable @@ -14,7 +15,7 @@ - + diff --git a/examples/Actor/DemoActor/Program.cs b/examples/Actor/DemoActor/Program.cs index efb81adee..575bc7183 100644 --- a/examples/Actor/DemoActor/Program.cs +++ b/examples/Actor/DemoActor/Program.cs @@ -11,22 +11,23 @@ // limitations under the License. // ------------------------------------------------------------------------ -using Microsoft.AspNetCore.Hosting; +using DemoActor; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace DemoActor; +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddSingleton(); +builder.Services.AddActors(options => +{ + options.Actors.RegisterActor(); +}); -public class Program +var app = builder.Build(); +if (app.Environment.IsDevelopment()) { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } + app.UseDeveloperExceptionPage(); +} - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); -} \ No newline at end of file +app.UseRouting(); +app.MapActorsHandlers(); diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs deleted file mode 100644 index 6cde6953b..000000000 --- a/examples/Actor/DemoActor/Startup.cs +++ /dev/null @@ -1,58 +0,0 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 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 Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace DemoActor; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddActors(options => - { - options.Actors.RegisterActor(); - }); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseHsts(); - } - - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers(); - }); - } -} \ No newline at end of file From 16386031e572299cf048013c1268028bf51f335c Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 1 Apr 2025 09:20:42 -0500 Subject: [PATCH 12/12] Modernized example Signed-off-by: Whit Waldo --- .../BulkPublishEventExample.cs | 6 +-- .../BulkPublishEventExample/Program.cs | 44 ++++++++----------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs index 90b5d9c68..af1fc639f 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.cs @@ -19,7 +19,7 @@ namespace Samples.Client; -public class BulkPublishEventExample : Example +public sealed class BulkPublishEventExample : Example { private const string PubsubName = "pubsub"; private const string TopicName = "deposit"; @@ -37,7 +37,7 @@ public override async Task RunAsync(CancellationToken cancellationToken) using var client = new DaprClientBuilder().Build(); var res = await client.BulkPublishEventAsync(PubsubName, TopicName, - BulkPublishData); + BulkPublishData, cancellationToken: cancellationToken); if (res != null) { if (res.FailedEntries.Count > 0) @@ -58,4 +58,4 @@ public override async Task RunAsync(CancellationToken cancellationToken) throw new Exception("null response from dapr"); } } -} \ No newline at end of file +} diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs b/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs index b540c6189..768f5ad4f 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/Program.cs @@ -13,34 +13,26 @@ using System; using System.Threading; -using System.Threading.Tasks; +using Samples.Client; -namespace Samples.Client; +Example[] Examples = +[ + new BulkPublishEventExample() +]; -class Program +if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) { - private static readonly Example[] Examples = new Example[] - { - new BulkPublishEventExample(), - }; + var cts = new CancellationTokenSource(); + Console.CancelKeyPress += (_, _) => cts.Cancel(); - static async Task Main(string[] args) - { - if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length) - { - var cts = new CancellationTokenSource(); - Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel(); + await Examples[index].RunAsync(cts.Token); + return 0; +} - await Examples[index].RunAsync(cts.Token); - return 0; - } - - Console.WriteLine("Hello, please choose a sample to run:"); - for (var i = 0; i < Examples.Length; i++) - { - Console.WriteLine($"{i}: {Examples[i].DisplayName}"); - } - Console.WriteLine(); - return 0; - } -} \ No newline at end of file +Console.WriteLine("Hello, please choose a sample to run:"); +for (var i = 0; i < Examples.Length; i++) +{ + Console.WriteLine($"{i}: {Examples[i].DisplayName}"); +} +Console.WriteLine(); +return 0;