From 1e148874bb3778d254d9a31f3a4ec6c33caafc0a Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 18 Oct 2024 02:56:12 -0500 Subject: [PATCH 1/7] Updating actor serialization documentation (#1371) * Changed headers, updated introduction to reflect the difference in serialization between either type and added a brief section to detail the use of System.Text.Json for weakly-typed Dapr actor clients and to point to official documentation on it --------- Signed-off-by: Whit Waldo --- .../dotnet-actors-serialization.md | 278 +++++++++++++++++- 1 file changed, 263 insertions(+), 15 deletions(-) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md index abbeb437d..787a7e41f 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md @@ -5,15 +5,263 @@ linkTitle: "Actor serialization" weight: 300000 description: Necessary steps to serialize your types using remoted Actors in .NET --- +# Actor Serialization -The Dapr actor package enables you to use Dapr virtual actors within a .NET application with strongly-typed remoting, but if you intend to send and receive strongly-typed data from your methods, there are a few key ground rules to understand. In this guide, you will learn how to configure your classes and records so they are properly serialized and deserialized at runtime. +The Dapr actor package enables you to use Dapr virtual actors within a .NET application with either a weakly- or strongly-typed client. Each utilizes a different serialization approach. This document will review the differences and convey a few key ground rules to understand in either scenario. -# Data Contract Serialization -When Dapr's virtual actors are invoked via the remoting proxy, your data is serialized using a serialization engine called the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) implemented by the [DataContractSerializer](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractserializer) class, which converts your C# types to and from XML documents. When sending or receiving primitives (like strings or ints), this serialization happens transparently and there's no requisite preparation needed on your part. However, when working with complex types such as those you create, there are some important rules to take into consideration so this process works smoothly. +Please be advised that it is not a supported scenario to use the weakly- or strongly typed actor clients interchangeably because of these different serialization approaches. The data persisted using one Actor client will not be accessible using the other Actor client, so it is important to pick one and use it consistently throughout your application. -This serialization framework is not specific to Dapr and is separately maintained by the .NET team within the [.NET Github repository](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs). +## Weakly-typed Dapr Actor client +In this section, you will learn how to configure your C# types so they are properly serialized and deserialized at runtime when using a weakly-typed actor client. These clients use string-based names of methods with request and response payloads that are serialized using the System.Text.Json serializer. Please note that this serialization framework is not specific to Dapr and is separately maintained by the .NET team within the [.NET GitHub repository](https://github.com/dotnet/runtime/tree/main/src/libraries/System.Text.Json). -## Serializable Types +When using the weakly-typed Dapr Actor client to invoke methods from your various actors, it's not necessary to independently serialize or deserialize the method payloads as this will happen transparently on your behalf by the SDK. + +The client will use the latest version of System.Text.Json available for the version of .NET you're building against and serialization is subject to all the inherent capabilities provided in the [associated .NET documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview). + +The serializer will be configured to use the `JsonSerializerOptions.Web` [default options](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/configure-options?pivots=dotnet-8-0#web-defaults-for-jsonserializeroptions) unless overridden with a custom options configuration which means the following are applied: +- Deserialization of the property name is performed in a case-insensitive manner +- Serialization of the property name is performed using [camel casing](https://en.wikipedia.org/wiki/Camel_case) unless the property is overridden with a `[JsonPropertyName]` attribute +- Deserialization will read numeric values from number and/or string values + +### Basic Serialization +In the following example, we present a simple class named Doodad though it could just as well be a record as well. + +```csharp +public class Doodad +{ + public Guid Id { get; set; } + public string Name { get; set; } + public int Count { get; set; } +} +``` + +By default, this will serialize using the names of the members as used in the type and whatever values it was instantiated with: + +```json +{"id": "a06ced64-4f42-48ad-84dd-46ae6a7e333d", "name": "DoodadName", "count": 5} +``` + +### Override Serialized Property Name +The default property names can be overridden by applying the `[JsonPropertyName]` attribute to desired properties. + +Generally, this isn't going to be necessary for types you're persisting to the actor state as you're not intended to read or write them independent of Dapr-associated functionality, but +the following is provided just to clearly illustrate that it's possible. + +#### Override Property Names on Classes +Here's an example demonstrating the use of `JsonPropertyName` to change the name for the first property following serialization. Note that the last usage of `JsonPropertyName` on the `Count` property +matches what it would be expected to serialize to. This is largely just to demonstrate that applying this attribute won't negatively impact anything - in fact, it might be preferable if you later +decide to change the default serialization options but still need to consistently access the properties previously serialized before that change as `JsonPropertyName` will override those options. + +```csharp +public class Doodad +{ + [JsonPropertyName("identifier")] + public Guid Id { get; set; } + public string Name { get; set; } + [JsonPropertyName("count")] + public int Count { get; set; } +} +``` + +This would serialize to the following: + +```json +{"identifier": "a06ced64-4f42-48ad-84dd-46ae6a7e333d", "name": "DoodadName", "count": 5} +``` + +#### Override Property Names on Records +Let's try doing the same thing with a record from C# 12 or later: + +```csharp +public record Thingy(string Name, [JsonPropertyName("count")] int Count); +``` + +Because the argument passed in a primary constructor (introduced in C# 12) can be applied to either a property or field within a record, using the `[JsonPropertyName]` attribute may +require specifying that you intend the attribute to apply to a property and not a field in some ambiguous cases. Should this be necessary, you'd indicate as much in the primary constructor with: + +```csharp +public record Thingy(string Name, [property: JsonPropertyName("count")] int Count); +``` + +If `[property: ]` is applied to the `[JsonPropertyName]` attribute where it's not necessary, it will not negatively impact serialization or deserialization as the operation will +proceed normally as though it were a property (as it typically would if not marked as such). + +### Enumeration types +Enumerations, including flat enumerations are serializable to JSON, but the value persisted may surprise you. Again, it's not expected that the developer should ever engage +with the serialized data independently of Dapr, but the following information may at least help in diagnosing why a seemingly mild version migration isn't working as expected. + +Take the following `enum` type providing the various seasons in the year: + +```csharp +public enum Season +{ + Spring, + Summer, + Fall, + Winter +} +``` + +We'll go ahead and use a separate demonstration type that references our `Season` and simultaneously illustrate how this works with records: + +```csharp +public record Engagement(string Name, Season TimeOfYear); +``` + +Given the following initialized instance: + +```csharp +var myEngagement = new Engagement("Ski Trip", Season.Winter); +``` + +This would serialize to the following JSON: +```json +{"name": "Ski Trip", "season": 3} +``` + +That might be unexpected that our `Season.Winter` value was represented as a `3`, but this is because the serializer is going to automatically use numeric representations +of the enum values starting with zero for the first value and incrementing the numeric value for each additional value available. Again, if a migration were taking place and +a developer had flipped the order of the enums, this would affect a breaking change in your solution as the serialized numeric values would point to different values when deserialized. + +Rather, there is a `JsonConverter` available with `System.Text.Json` that will instead opt to use a string-based value instead of the numeric value. The `[JsonConverter]` attribute needs +to be applied to be enum type itself to enable this, but will then be realized in any downstream serialization or deserialization operation that references the enum. + +```csharp +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum Season +{ + Spring, + Summer, + Fall, + Winter +} +``` + +Using the same values from our `myEngagement` instance above, this would produce the following JSON instead: + +```json +{"name": "Ski Trip", "season": "Winter"} +``` + +As a result, the enum members can be shifted around without fear of introducing errors during deserialization. + +#### Custom Enumeration Values + +The System.Text.Json serialization platform doesn't, out of the box, support the use of `[EnumMember]` to allow you to change the value of enum that's used during serialization or deserialization, but +there are scenarios where this could be useful. Again, assume that you're tasking with refactoring the solution to apply some better names to your various +enums. You're using the `JsonStringEnumConverter` detailed above so you're saving the name of the enum to value instead of a numeric value, but if you change +the enum name, that will introduce a breaking change as the name will no longer match what's in state. + +Do note that if you opt into using this approach, you should decorate all your enum members with the `[EnumMeber]` attribute so that the values are consistently applied for each enum value instead +of haphazardly. Nothing will validate this at build or runtime, but it is considered a best practice operation. + +How can you specify the precise value persisted while still changing the name of the enum member in this scenario? Use a custom `JsonConverter` with an extension method that can pull the value +out of the attached `[EnumMember]` attributes where provided. Add the following to your solution: + +```csharp +public sealed class EnumMemberJsonConverter : JsonConverter where T : struct, Enum +{ + /// Reads and converts the JSON to type . + /// The reader. + /// The type to convert. + /// An object that specifies serialization options to use. + /// The converted value. + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Get the string value from the JSON reader + var value = reader.GetString(); + + // Loop through all the enum values + foreach (var enumValue in Enum.GetValues()) + { + // Get the value from the EnumMember attribute, if any + var enumMemberValue = GetValueFromEnumMember(enumValue); + + // If the values match, return the enum value + if (value == enumMemberValue) + { + return enumValue; + } + } + + // If no match found, throw an exception + throw new JsonException($"Invalid value for {typeToConvert.Name}: {value}"); + } + + /// Writes a specified value as JSON. + /// The writer to write to. + /// The value to convert to JSON. + /// An object that specifies serialization options to use. + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + // Get the value from the EnumMember attribute, if any + var enumMemberValue = GetValueFromEnumMember(value); + + // Write the value to the JSON writer + writer.WriteStringValue(enumMemberValue); + } + + private static string GetValueFromEnumMember(T value) + { + MemberInfo[] member = typeof(T).GetMember(value.ToString(), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public); + if (member.Length == 0) + return value.ToString(); + object[] customAttributes = member.GetCustomAttributes(typeof(EnumMemberAttribute), false); + if (customAttributes.Length != 0) + { + EnumMemberAttribute enumMemberAttribute = (EnumMemberAttribute)customAttributes; + if (enumMemberAttribute != null && enumMemberAttribute.Value != null) + return enumMemberAttribute.Value; + } + return value.ToString(); + } +} +``` + +Now let's add a sample enumerator. We'll set a value that uses the lower-case version of each enum member to demonstrate this. Don't forget to decorate the enum with the `JsonConverter` +attribute and reference our custom converter in place of the numeral-to-string converter used in the last section. + +```csharp +[JsonConverter(typeof(EnumMemberJsonConverter))] +public enum Season +{ + [EnumMember(Value="spring")] + Spring, + [EnumMember(Value="summer")] + Summer, + [EnumMember(Value="fall")] + Fall, + [EnumMember(Value="winter")] + Winter +} +``` + +Let's use our sample record from before. We'll also add a `[JsonPropertyName]` attribute just to augment the demonstration: +```csharp +public record Engagement([property: JsonPropertyName("event")] string Name, Season TimeOfYear); +``` + +And finally, let's initialize a new instance of this: + +```csharp +var myEngagement = new Engagement("Conference", Season.Fall); +``` + +This time, serialization will take into account the values from the attached `[EnumMember]` attribute providing us a mechanism to refactor our application without necessitating +a complex versioning scheme for our existing enum values in the state. + +```json +{"event": "Conference", "season": "fall"} +``` + +## Strongly-typed Dapr Actor client +In this section, you will learn how to configure your classes and records so they are properly serialized and deserialized at runtime when using a strongly-typed actor client. These clients are implemented using .NET interfaces and are not compatible with Dapr Actors written using other languages. + +This actor client serializes data using an engine called the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) which converts your C# types to and from XML documents. This serialization framework is not specific to Dapr and is separately maintained by the .NET team within the [.NET GitHub repository](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs). + +When sending or receiving primitives (like strings or ints), this serialization happens transparently and there's no requisite preparation needed on your part. However, when working with complex types such as those you create, there are some important rules to take into consideration so this process works smoothly. + +### Serializable Types There are several important considerations to keep in mind when using the Data Contract Serializer: - By default, all types, read/write properties (after construction) and fields marked as publicly visible are serialized @@ -23,14 +271,14 @@ There are several important considerations to keep in mind when using the Data C - Serialization is supported for types that use other complex types that are not themselves marked with the DataContractAttribute attribute through the use of the KnownTypesAttribute attribute - If a type is marked with the DataContractAttribute attribute, all members you wish to serialize and deserialize must be decorated with the DataMemberAttribute attribute as well or they'll be set to their default values -## How does deserialization work? +### How does deserialization work? The approach used for deserialization depends on whether or not the type is decorated with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. If this attribute isn't present, an instance of the type is created using the parameterless constructor. Each of the properties and fields are then mapped into the type using their respective setters and the instance is returned to the caller. If the type _is_ marked with `[DataContract]`, the serializer instead uses reflection to read the metadata of the type and determine which properties or fields should be included based on whether or not they're marked with the DataMemberAttribute attribute as it's performed on an opt-in basis. It then allocates an uninitialized object in memory (avoiding the use of any constructors, parameterless or not) and then sets the value directly on each mapped property or field, even if private or uses init-only setters. Serialization callbacks are invoked as applicable throughout this process and then the object is returned to the caller. Use of the serialization attributes is highly recommended as they grant more flexibility to override names and namespaces and generally use more of the modern C# functionality. While the default serializer can be relied on for primitive types, it's not recommended for any of your own types, whether they be classes, structs or records. It's recommended that if you decorate a type with the DataContractAttribute attribute, you also explicitly decorate each of the members you want to serialize or deserialize with the DataMemberAttribute attribute as well. -### .NET Classes +#### .NET Classes Classes are fully supported in the Data Contract Serializer provided that that other rules detailed on this page and the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) documentation are also followed. The most important thing to remember here is that you must either have a public parameterless constructor or you must decorate it with the appropriate attributes. Let's review some examples to really clarify what will and won't work. @@ -153,7 +401,7 @@ When this is serialized, because we're changing the names of the serialized memb ``` -#### Classes in C# 12 - Primary Constructors +##### Classes in C# 12 - Primary Constructors C# 12 brought us primary constructors on classes. Use of a primary constructor means the compiler will be prevented from creating the default implicit parameterless constructor. While a primary constructor on a class doesn't generate any public properties, it does mean that if you pass this primary constructor any arguments or have non-primitive types in your class, you'll either need to specify your own parameterless constructor or use the serialization attributes. Here's an example where we're using the primary constructor to inject an ILogger to a field and add our own parameterless constructor without the need for any attributes. @@ -198,7 +446,7 @@ public class Doodad(ILogger _logger) } ``` -### .NET Structs +#### .NET Structs Structs are supported by the Data Contract serializer provided that they are marked with the DataContractAttribute attribute and the members you wish to serialize are marked with the DataMemberAttribute attribute. Further, to support deserialization, the struct will also need to have a parameterless constructor. This works even if you define your own parameterless constructor as enabled in C# 10. ```csharp @@ -210,7 +458,7 @@ public struct Doodad } ``` -### .NET Records +#### .NET Records Records were introduced in C# 9 and follow precisely the same rules as classes when it comes to serialization. We recommend that you should decorate all your records with the DataContractAttribute attribute and members you wish to serialize with DataMemberAttribute attributes so you don't experience any deserialization issues using this or other newer C# functionalities. Because record classes use init-only setters for properties by default and encourage the use of the primary constructor, applying these attributes to your types ensures that the serializer can properly otherwise accommodate your types as-is. Typically records are presented as a simple one-line statement using the new primary constructor concept: @@ -238,7 +486,7 @@ public record Doodad( [property: DataMember] int Count) ``` -### Supported Primitive Types +#### Supported Primitive Types There are several types built into .NET that are considered primitive and eligible for serialization without additional effort on the part of the developer: - [Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte) @@ -267,7 +515,7 @@ There are additional types that aren't actually primitives but have similar buil Again, if you want to pass these types around via your actor methods, no additional consideration is necessary as they'll be serialized and deserialized without issue. Further, types that are themselves marked with the (SerializeableAttribute)[https://learn.microsoft.com/en-us/dotnet/api/system.serializableattribute] attribute will be serialized. -### Enumeration Types +#### Enumeration Types Enumerations, including flag enumerations are serializable if appropriately marked. The enum members you wish to be serialized must be marked with the [EnumMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.enummemberattribute) attribute in order to be serialized. Passing a custom value into the optional Value argument on this attribute will allow you to specify the value used for the member in the serialized document instead of having the serializer derive it from the name of the member. The enum type does not require that the type be decorated with the `DataContractAttribute` attribute - only that the members you wish to serialize be decorated with the `EnumMemberAttribute` attributes. @@ -283,15 +531,15 @@ public enum Colors } ``` -### Collection Types +#### Collection Types With regards to the data contact serializer, all collection types that implement the [IEnumerable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.ienumerable) interface including arays and generic collections are considered collections. Those types that implement [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.idictionary) or the generic [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2) are considered dictionary collections; all others are list collections. Not unlike other complex types, collection types must have a parameterless constructor available. Further, they must also have a method called Add so they can be properly serialized and deserialized. The types used by these collection types must themselves be marked with the `DataContractAttribute` attribute or otherwise be serializable as described throughout this document. -### Data Contract Versioning +#### Data Contract Versioning As the data contract serializer is only used in Dapr with respect to serializing the values in the .NET SDK to and from the Dapr actor instances via the proxy methods, there's little need to consider versioning of data contracts as the data isn't being persisted between application versions using the same serializer. For those interested in learning more about data contract versioning visit [here](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-versioning). -### Known Types +#### Known Types Nesting your own complex types is easily accommodated by marking each of the types with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. This informs the serializer as to how deserialization should be performed. But what if you're working with polymorphic types and one of your members is a base class or interface with derived classes or other implementations? Here, you'll use the [KnownTypeAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.knowntypeattribute) attribute to give a hint to the serializer about how to proceed. From 5548c670f480a9eb7c13efb35899ef959636f5dd Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 18 Oct 2024 03:20:01 -0500 Subject: [PATCH 2/7] Prioritize retrieval of environment variables from IConfiguration instead of directly (#1363) * Implemented against Dapr.Client.AspNetCore and Dapr.Client Signed-off-by: Whit Waldo * SImplified DaprWorkflow DI registration and updated to use IConfiguration preference. Needs testing. Signed-off-by: Whit Waldo * Added missing copyright header Signed-off-by: Whit Waldo * Updated actor registration to prefer the updated IConfiguration-based approach for pulling the HTTP endpoint and API token Signed-off-by: Whit Waldo * Adopted accepted proposal's guidelines for favoring different environment variables for determining the sidecar endpoint. Added notes to explain this in the code going forward. Signed-off-by: Whit Waldo * Made some lines a little more concise, added hostname default to DaprDefaults to use when building endpoints. Signed-off-by: Whit Waldo * Fixed and updated unit tests Signed-off-by: Whit Waldo * Updated to put endpoint resolution mechanism in DaprDefaults within Dapr.Common - updating projects and unit tests Signed-off-by: Whit Waldo * Updated packages to fix security advisory https://github.com/advisories/GHSA-447r-wph3-92pm Signed-off-by: Whit Waldo * Updated Workflow builder to use DaprDefaults with IConfiguration Signed-off-by: Whit Waldo * Updating global.json Signed-off-by: Whit Waldo * Tweaked global.json comment Signed-off-by: Whit Waldo * Adding braces per nit Signed-off-by: Whit Waldo * Consolidated both registration extension methods to remove duplication Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo --- Directory.Packages.props | 3 + all.sln | 7 + global.json | 6 +- .../ActorsServiceCollectionExtensions.cs | 28 +- .../Dapr.Actors.AspNetCore.csproj | 1 + src/Dapr.Actors/Client/ActorProxyOptions.cs | 2 +- src/Dapr.Actors/Dapr.Actors.csproj | 1 + .../Runtime/ActorRuntimeOptions.cs | 22 +- .../DaprAuthenticationOptions.cs | 3 +- .../DaprServiceCollectionExtensions.cs | 101 +-- src/Dapr.Client/DaprClient.cs | 11 +- src/Dapr.Client/DaprClientBuilder.cs | 2 +- src/Dapr.Client/InvocationHandler.cs | 2 +- src/Dapr.Common/Dapr.Common.csproj | 2 + src/Dapr.Common/DaprDefaults.cs | 131 ++-- .../DaprWorkflowClientBuilderFactory.cs | 95 +++ .../WorkflowServiceCollectionExtensions.cs | 135 +--- .../Dapr.Actors.Generators.Test.csproj | 1 + .../DaprServiceCollectionExtensionsTest.cs | 25 +- test/Dapr.Common.Test/Dapr.Common.Test.csproj | 23 + test/Dapr.Common.Test/DaprDefaultTest.cs | 582 ++++++++++++++++++ 21 files changed, 918 insertions(+), 265 deletions(-) create mode 100644 src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs create mode 100644 test/Dapr.Common.Test/Dapr.Common.Test.csproj create mode 100644 test/Dapr.Common.Test/DaprDefaultTest.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index d85020770..842c82aa8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,7 @@ + @@ -26,6 +27,7 @@ + @@ -39,6 +41,7 @@ + diff --git a/all.sln b/all.sln index 1a5d78efb..85ed848a4 100644 --- a/all.sln +++ b/all.sln @@ -122,6 +122,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Protos", "src\Dapr.Pro EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common", "src\Dapr.Common\Dapr.Common.csproj", "{B445B19C-A925-4873-8CB7-8317898B6970}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common.Test", "test\Dapr.Common.Test\Dapr.Common.Test.csproj", "{CDB47863-BEBD-4841-A807-46D868962521}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -302,6 +304,10 @@ Global {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.Build.0 = Debug|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.ActiveCfg = Release|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.Build.0 = Release|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -357,6 +363,7 @@ Global {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} {DFBABB04-50E9-42F6-B470-310E1B545638} = {27C5D71D-0721-4221-9286-B94AB07B58CF} {B445B19C-A925-4873-8CB7-8317898B6970} = {27C5D71D-0721-4221-9286-B94AB07B58CF} + {CDB47863-BEBD-4841-A807-46D868962521} = {DD020B34-460F-455F-8D17-CF4A949F100B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} diff --git a/global.json b/global.json index 980f4652d..fe53f92ae 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { - "_comment": "This policy allows the 7.0.101 SDK or patches in that family.", + "_comment": "This policy allows the 8.0.100 SDK or patches in that family.", "sdk": { - "version": "7.0.101", - "rollForward": "latestMajor" + "version": "8.0.100", + "rollForward": "minor" } } \ No newline at end of file diff --git a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs index a7f5d04a2..11f05f4c1 100644 --- a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs +++ b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -11,9 +11,13 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable + using System; +using Dapr; using Dapr.Actors.Client; using Dapr.Actors.Runtime; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -30,12 +34,9 @@ public static class ActorsServiceCollectionExtensions /// /// The . /// A delegate used to configure actor options and register actor types. - public static void AddActors(this IServiceCollection services, Action configure) + public static void AddActors(this IServiceCollection? services, Action? configure) { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } + ArgumentNullException.ThrowIfNull(services, nameof(services)); // Routing and health checks are required dependencies. services.AddRouting(); @@ -45,6 +46,8 @@ public static void AddActors(this IServiceCollection services, Action(s => { var options = s.GetRequiredService>().Value; + ConfigureActorOptions(s, options); + var loggerFactory = s.GetRequiredService(); var activatorFactory = s.GetRequiredService(); var proxyFactory = s.GetRequiredService(); @@ -54,6 +57,8 @@ public static void AddActors(this IServiceCollection services, Action(s => { var options = s.GetRequiredService>().Value; + ConfigureActorOptions(s, options); + var factory = new ActorProxyFactory() { DefaultOptions = @@ -72,5 +77,16 @@ public static void AddActors(this IServiceCollection services, Action(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(); + } } } diff --git a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj index 82c5863db..ef57e76fe 100644 --- a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj +++ b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj @@ -11,5 +11,6 @@ + diff --git a/src/Dapr.Actors/Client/ActorProxyOptions.cs b/src/Dapr.Actors/Client/ActorProxyOptions.cs index 665a1dced..2afce852c 100644 --- a/src/Dapr.Actors/Client/ActorProxyOptions.cs +++ b/src/Dapr.Actors/Client/ActorProxyOptions.cs @@ -45,7 +45,7 @@ public JsonSerializerOptions JsonSerializerOptions /// /// The Dapr Api Token that is added to the header for all requests. /// - public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(); + public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(null); /// /// Gets or sets the HTTP endpoint URI used to communicate with the Dapr sidecar. diff --git a/src/Dapr.Actors/Dapr.Actors.csproj b/src/Dapr.Actors/Dapr.Actors.csproj index 54d3487b8..bcb8d830f 100644 --- a/src/Dapr.Actors/Dapr.Actors.csproj +++ b/src/Dapr.Actors/Dapr.Actors.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs index 62eaceea6..21c302018 100644 --- a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs +++ b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable + using System; using System.Text.Json; @@ -34,7 +36,7 @@ public sealed class ActorRuntimeOptions }; private bool useJsonSerialization = false; private JsonSerializerOptions jsonSerializerOptions = JsonSerializerDefaults.Web; - private string daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); + private string daprApiToken = string.Empty; private int? remindersStoragePartitions = null; /// @@ -180,19 +182,14 @@ public JsonSerializerOptions JsonSerializerOptions set { - if (value is null) - { - throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorRuntimeOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); - } - - this.jsonSerializerOptions = value; + 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 + public string? DaprApiToken { get { @@ -201,12 +198,7 @@ public string DaprApiToken set { - if (value is null) - { - throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); - } - - this.daprApiToken = value; + this.daprApiToken = value ?? throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); } } @@ -241,6 +233,6 @@ public int? RemindersStoragePartitions /// corresponding environment variables. /// /// - public string HttpEndpoint { get; set; } = DaprDefaults.GetDefaultHttpEndpoint(); + public string? HttpEndpoint { get; set; } } } diff --git a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs index 4e073d06c..b12d4d14e 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs @@ -11,7 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; using Microsoft.AspNetCore.Authentication; namespace Dapr.AspNetCore @@ -29,6 +28,6 @@ public class DaprAuthenticationOptions : AuthenticationSchemeOptions /// 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(); + public string Token { get; set; } = DaprDefaults.GetDefaultAppApiToken(null); } } diff --git a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs index 388015b80..52e9110be 100644 --- a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs +++ b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -11,55 +11,76 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.Extensions.DependencyInjection -{ - using System; - using System.Linq; - using Dapr.Client; - using Extensions; +#nullable enable + +namespace Microsoft.Extensions.DependencyInjection; + +using System; +using Dapr; +using Dapr.Client; +using Extensions; +using Configuration; +/// +/// Provides extension methods for . +/// +public static class DaprServiceCollectionExtensions +{ /// - /// Provides extension methods for . + /// Adds Dapr client services to the provided . This does not include integration + /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. /// - public static class DaprServiceCollectionExtensions + /// The . + /// + public static void AddDaprClient(this IServiceCollection services, Action? configure = null) { - /// - /// Adds Dapr client services to the provided . This does not include integration - /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. - /// - /// The . - /// - public static void AddDaprClient(this IServiceCollection services, Action configure = null) + ArgumentNullException.ThrowIfNull(services, nameof(services)); + + services.TryAddSingleton(serviceProvider => { - ArgumentNullException.ThrowIfNull(services, nameof(services)); + var builder = CreateDaprClientBuilder(serviceProvider); + configure?.Invoke(builder); + return builder.Build(); + }); + } - services.TryAddSingleton(_ => - { - var builder = new DaprClientBuilder(); - configure?.Invoke(builder); + /// + /// Adds Dapr client services to the provided . This does not include integration + /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. + /// + /// The . + /// + public static void AddDaprClient(this IServiceCollection services, + Action configure) + { + ArgumentNullException.ThrowIfNull(services, nameof(services)); - return builder.Build(); - }); - } - - /// - /// Adds Dapr client services to the provided . This does not include integration - /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. - /// - /// The . - /// - public static void AddDaprClient(this IServiceCollection services, - Action configure) + services.TryAddSingleton(serviceProvider => { - ArgumentNullException.ThrowIfNull(services, nameof(services)); + var builder = CreateDaprClientBuilder(serviceProvider); + configure?.Invoke(serviceProvider, builder); + return builder.Build(); + }); + } + + private static DaprClientBuilder CreateDaprClientBuilder(IServiceProvider serviceProvider) + { + var builder = new DaprClientBuilder(); + var configuration = serviceProvider.GetService(); + + // Set the HTTP endpoint, if provided, else use the default endpoint + builder.UseHttpEndpoint(DaprDefaults.GetDefaultHttpEndpoint(configuration)); - services.TryAddSingleton(serviceProvider => - { - var builder = new DaprClientBuilder(); - configure?.Invoke(serviceProvider, builder); + // Set the gRPC endpoint, if provided + builder.UseGrpcEndpoint(DaprDefaults.GetDefaultGrpcEndpoint(configuration)); - return builder.Build(); - }); + // Set the API token, if provided + var apiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + if (!string.IsNullOrWhiteSpace(apiToken)) + { + builder.UseDaprApiToken(apiToken); } + + return builder; } } diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 9f107578f..43c640a69 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -139,17 +139,14 @@ public static HttpClient CreateInvokeHttpClient(string appId = null, string dapr 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())); + return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null))); } internal static KeyValuePair? GetDaprApiTokenHeader(string apiToken) { - if (string.IsNullOrWhiteSpace(apiToken)) - { - return null; - } - - return new KeyValuePair("dapr-api-token", apiToken); + return string.IsNullOrWhiteSpace(apiToken) + ? null + : new KeyValuePair("dapr-api-token", apiToken); } /// diff --git a/src/Dapr.Client/DaprClientBuilder.cs b/src/Dapr.Client/DaprClientBuilder.cs index 50a4979d1..68315c45b 100644 --- a/src/Dapr.Client/DaprClientBuilder.cs +++ b/src/Dapr.Client/DaprClientBuilder.cs @@ -40,7 +40,7 @@ public DaprClientBuilder() }; this.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); - this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(); + this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(null); } // property exposed for testing purposes diff --git a/src/Dapr.Client/InvocationHandler.cs b/src/Dapr.Client/InvocationHandler.cs index 1b55436aa..36fd6b77f 100644 --- a/src/Dapr.Client/InvocationHandler.cs +++ b/src/Dapr.Client/InvocationHandler.cs @@ -51,7 +51,7 @@ public class InvocationHandler : DelegatingHandler public InvocationHandler() { this.parsedEndpoint = new Uri(DaprDefaults.GetDefaultHttpEndpoint(), UriKind.Absolute); - this.apiToken = DaprDefaults.GetDefaultDaprApiToken(); + this.apiToken = DaprDefaults.GetDefaultDaprApiToken(null); } /// diff --git a/src/Dapr.Common/Dapr.Common.csproj b/src/Dapr.Common/Dapr.Common.csproj index ea3e8ae84..31af3952c 100644 --- a/src/Dapr.Common/Dapr.Common.csproj +++ b/src/Dapr.Common/Dapr.Common.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/Dapr.Common/DaprDefaults.cs b/src/Dapr.Common/DaprDefaults.cs index 575a3c148..85a4b18c8 100644 --- a/src/Dapr.Common/DaprDefaults.cs +++ b/src/Dapr.Common/DaprDefaults.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Microsoft.Extensions.Configuration; + namespace Dapr { internal static class DaprDefaults @@ -20,82 +22,111 @@ internal static class DaprDefaults 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 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() - { - // Lazy-init is safe because this is just populating the default - // We don't plan to support the case where the user changes environment variables - // for a running process. - if (string.IsNullOrEmpty(daprApiToken)) - { - // Treat empty the same as null since it's an environment variable - var value = Environment.GetEnvironmentVariable("DAPR_API_TOKEN"); - daprApiToken = string.IsNullOrEmpty(value) ? string.Empty : value; - } - - return daprApiToken; - } + 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() - { - if (string.IsNullOrEmpty(appApiToken)) - { - var value = Environment.GetEnvironmentVariable("APP_API_TOKEN"); - appApiToken = string.IsNullOrEmpty(value) ? string.Empty : value; - } - - return appApiToken; - } + 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() + public static string GetDefaultHttpEndpoint(IConfiguration? configuration = null) { - if (string.IsNullOrEmpty(httpEndpoint)) - { - var endpoint = Environment.GetEnvironmentVariable("DAPR_HTTP_ENDPOINT"); - if (!string.IsNullOrEmpty(endpoint)) { - httpEndpoint = endpoint; - return httpEndpoint; - } - - var port = Environment.GetEnvironmentVariable("DAPR_HTTP_PORT"); - port = string.IsNullOrEmpty(port) ? "3500" : port; - httpEndpoint = $"http://127.0.0.1:{port}"; - } + //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); - return httpEndpoint; + 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() + public static string GetDefaultGrpcEndpoint(IConfiguration? configuration = null) { - if (string.IsNullOrEmpty(grpcEndpoint)) - { - var endpoint = Environment.GetEnvironmentVariable("DAPR_GRPC_ENDPOINT"); - if (!string.IsNullOrEmpty(endpoint)) { - grpcEndpoint = endpoint; - return grpcEndpoint; - } + //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); + + 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 - var port = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - port = string.IsNullOrEmpty(port) ? "50001" : port; - grpcEndpoint = $"http://127.0.0.1:{port}"; + 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; + } + + 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; - return grpcEndpoint; + //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.Workflow/DaprWorkflowClientBuilderFactory.cs b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs new file mode 100644 index 000000000..7a854cf05 --- /dev/null +++ b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------ +// Copyright 2024 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------ + +using System; +using System.Net.Http; +using Grpc.Net.Client; +using Microsoft.DurableTask.Client; +using Microsoft.DurableTask.Worker; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +#nullable enable + +namespace Dapr.Workflow; + +/// +/// A factory for building a . +/// +internal sealed class DaprWorkflowClientBuilderFactory +{ + private readonly IConfiguration _configuration; + private readonly IHttpClientFactory _httpClientFactory; + private readonly IServiceCollection _services; + + /// + /// Constructor used to inject the required types into the factory. + /// + public DaprWorkflowClientBuilderFactory(IConfiguration configuration, IHttpClientFactory httpClientFactory, IServiceCollection services) + { + _configuration = configuration; + _httpClientFactory = httpClientFactory; + _services = services; + } + + /// + /// Responsible for building the client itself. + /// + /// + public void CreateClientBuilder(Action configure) + { + _services.AddDurableTaskClient(builder => + { + var apiToken = DaprDefaults.GetDefaultDaprApiToken(_configuration); + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(_configuration); + + var httpClient = _httpClientFactory.CreateClient(); + + if (!string.IsNullOrWhiteSpace(apiToken)) + { + httpClient.DefaultRequestHeaders.Add( "Dapr-Api-Token", apiToken); + } + + builder.UseGrpc(GrpcChannel.ForAddress(grpcEndpoint, new GrpcChannelOptions { HttpClient = httpClient })); + builder.RegisterDirectly(); + }); + + _services.AddDurableTaskWorker(builder => + { + WorkflowRuntimeOptions options = new(); + configure?.Invoke(options); + + var apiToken = DaprDefaults.GetDefaultDaprApiToken(_configuration); + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(_configuration); + + if (!string.IsNullOrEmpty(grpcEndpoint)) + { + var httpClient = _httpClientFactory.CreateClient(); + + if (!string.IsNullOrWhiteSpace(apiToken)) + { + httpClient.DefaultRequestHeaders.Add("Dapr-Api-Token", apiToken); + } + + builder.UseGrpc( + GrpcChannel.ForAddress(grpcEndpoint, new GrpcChannelOptions { HttpClient = httpClient })); + } + else + { + builder.UseGrpc(); + } + + builder.AddTasks(registry => options.AddWorkflowsAndActivitiesToRegistry(registry)); + }); + } +} diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index ca514f221..3c19583aa 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -14,20 +14,14 @@ namespace Dapr.Workflow { using System; - using Grpc.Net.Client; - using Microsoft.DurableTask.Client; - using Microsoft.DurableTask.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; - using System.Net.Http; - using Dapr; /// /// Contains extension methods for using Dapr Workflow with dependency injection. /// public static class WorkflowServiceCollectionExtensions { - /// /// Adds Dapr Workflow support to the service collection. /// @@ -43,6 +37,7 @@ public static IServiceCollection AddDaprWorkflow( } serviceCollection.TryAddSingleton(); + serviceCollection.AddHttpClient(); #pragma warning disable CS0618 // Type or member is obsolete - keeping around temporarily - replaced by DaprWorkflowClient serviceCollection.TryAddSingleton(); @@ -50,135 +45,17 @@ public static IServiceCollection AddDaprWorkflow( serviceCollection.AddHostedService(); serviceCollection.TryAddSingleton(); serviceCollection.AddDaprClient(); - serviceCollection.AddDaprWorkflowClient(); - + serviceCollection.AddOptions().Configure(configure); - serviceCollection.AddDurableTaskWorker(builder => + serviceCollection.AddSingleton(c => { - WorkflowRuntimeOptions options = new(); - configure?.Invoke(options); - - if (TryGetGrpcAddress(out string address)) - { - var daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); - if (!string.IsNullOrEmpty(daprApiToken)) - { - var client = new HttpClient(); - client.DefaultRequestHeaders.Add("Dapr-Api-Token", daprApiToken); - builder.UseGrpc(CreateChannel(address, client)); - } - else - { - builder.UseGrpc(address); - } - - } - else - { - builder.UseGrpc(); - } - - builder.AddTasks(registry => options.AddWorkflowsAndActivitiesToRegistry(registry)); + var factory = c.GetRequiredService(); + factory.CreateClientBuilder(configure); + return new object(); //Placeholder as actual registration is performed inside factory }); return serviceCollection; } - - /// - /// Adds Dapr Workflow client support to the service collection. - /// - /// - /// Use this extension method if you want to use in your app - /// but don't wish to define any workflows or activities. - /// - /// The . - public static IServiceCollection AddDaprWorkflowClient(this IServiceCollection services) - { - services.TryAddSingleton(); - services.AddDurableTaskClient(builder => - { - if (TryGetGrpcAddress(out string address)) - { - var daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); - if (!string.IsNullOrEmpty(daprApiToken)) - { - var client = new HttpClient(); - client.DefaultRequestHeaders.Add("Dapr-Api-Token", daprApiToken); - builder.UseGrpc(CreateChannel(address, client)); - } - else - { - builder.UseGrpc(address); - } - - } - else - { - builder.UseGrpc(); - } - - builder.RegisterDirectly(); - }); - - return services; - } - - static bool TryGetGrpcAddress(out string address) - { - // TODO: Ideally we should be using DaprDefaults.cs for this. However, there are two blockers: - // 1. DaprDefaults.cs uses 127.0.0.1 instead of localhost, which prevents testing with Dapr on WSL2 and the app on Windows - // 2. DaprDefaults.cs doesn't compile when the project has C# nullable reference types enabled. - // If the above issues are fixed (ensuring we don't regress anything) we should switch to using the logic in DaprDefaults.cs. - var daprEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); - if (!String.IsNullOrEmpty(daprEndpoint)) { - address = daprEndpoint; - return true; - } - - var daprPortStr = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - if (int.TryParse(daprPortStr, out int daprGrpcPort)) - { - // There is a bug in the Durable Task SDK that requires us to change the format of the address - // depending on the version of .NET that we're targeting. For now, we work around this manually. -#if NET6_0_OR_GREATER - address = $"http://localhost:{daprGrpcPort}"; -#else - address = $"localhost:{daprGrpcPort}"; -#endif - return true; - } - - address = string.Empty; - return false; - } - - static GrpcChannel CreateChannel(string address, HttpClient client) - { - - GrpcChannelOptions options = new() { HttpClient = client}; - var daprEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); - if (!String.IsNullOrEmpty(daprEndpoint)) { - return GrpcChannel.ForAddress(daprEndpoint, options); - } - - var daprPortStr = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - if (int.TryParse(daprPortStr, out int daprGrpcPort)) - { - // If there is no address passed in, we default to localhost - if (String.IsNullOrEmpty(address)) - { - // There is a bug in the Durable Task SDK that requires us to change the format of the address - // depending on the version of .NET that we're targeting. For now, we work around this manually. - #if NET6_0_OR_GREATER - address = $"http://localhost:{daprGrpcPort}"; - #else - address = $"localhost:{daprGrpcPort}"; - #endif - } - - } - return GrpcChannel.ForAddress(address, options); - } } } diff --git a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj index 91c7e8b42..80a79cafe 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -27,6 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs index a82948cf3..4a340e22a 100644 --- a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -11,7 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; +#nullable enable + +using System; using System.Text.Json; using Dapr.Client; using Microsoft.Extensions.DependencyInjection; @@ -43,15 +45,16 @@ public void AddDaprClient_RegistersDaprClientOnlyOnce() var serviceProvider = services.BuildServiceProvider(); - DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; + DaprClientGrpc? daprClient = serviceProvider.GetService() as DaprClientGrpc; - Assert.True(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); + Assert.NotNull(daprClient); + Assert.True(daprClient?.JsonSerializerOptions.PropertyNameCaseInsensitive); } [Fact] public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider() { - + var services = new ServiceCollection(); services.AddSingleton(); services.AddDaprClient((provider, builder) => @@ -66,13 +69,15 @@ public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider() }); var serviceProvider = services.BuildServiceProvider(); - - DaprClientGrpc client = serviceProvider.GetRequiredService() as DaprClientGrpc; - + + DaprClientGrpc? client = serviceProvider.GetRequiredService() as DaprClientGrpc; + //Registers with case-insensitive as true by default, but we set as false above - Assert.False(client.JsonSerializerOptions.PropertyNameCaseInsensitive); + Assert.NotNull(client); + Assert.False(client?.JsonSerializerOptions.PropertyNameCaseInsensitive); } + #if NET8_0_OR_GREATER [Fact] public void AddDaprClient_WithKeyedServices() @@ -90,7 +95,7 @@ public void AddDaprClient_WithKeyedServices() Assert.NotNull(daprClient); } #endif - + private class TestConfigurationProvider { public bool GetCaseSensitivity() => false; diff --git a/test/Dapr.Common.Test/Dapr.Common.Test.csproj b/test/Dapr.Common.Test/Dapr.Common.Test.csproj new file mode 100644 index 000000000..6e34c3a7a --- /dev/null +++ b/test/Dapr.Common.Test/Dapr.Common.Test.csproj @@ -0,0 +1,23 @@ + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/test/Dapr.Common.Test/DaprDefaultTest.cs b/test/Dapr.Common.Test/DaprDefaultTest.cs new file mode 100644 index 000000000..ef4d0da3c --- /dev/null +++ b/test/Dapr.Common.Test/DaprDefaultTest.cs @@ -0,0 +1,582 @@ +using System; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Dapr.Common.Test; + +public class DaprDefaultTest +{ + [Fact] + public void ShouldBuildHttpEndpointAndPortUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "test_DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointAndPortUsingConfiguration() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "test_DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingConfiguration() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingOnlyPortConfiguration() + { + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:2569/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingEnvVarValues() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "http://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://dapr.io:80/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingMixedValues() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + configurationBuilder.AddEnvironmentVariables("test_"); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToEmptyHttpEndpoint() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:3500/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToLocalhostWithPort() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, "7256"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:7256/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToLocalhostWithDefaultPort() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:3500/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_GRPC_ENDPOINT"; + const string portVarName = "test_DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2570"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingConfiguration() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable(portVarName, null, EnvironmentVariableTarget.Process); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_GRPC_ENDPOINT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingConfiguration() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingEnvVarValues() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "4744"); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingEnvVarValues() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointDefaultToLocalhostWithPort() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_grpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_grpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, "7256"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal($"{DaprDefaults.DefaultDaprScheme}://{DaprDefaults.DefaultDaprHost}:7256/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_grpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_grpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointDefaultToLocalhostWithDefaultPort() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_grpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_grpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal($"{DaprDefaults.DefaultDaprScheme}://{DaprDefaults.DefaultDaprHost}:{DaprDefaults.DefaultGrpcPort}/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_grpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_grpcPort); + } + } + + [Fact] + public void ShouldBuildApiTokenUsingConfiguration() + { + const string envVarName = DaprDefaults.DaprApiTokenName; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + + try + { + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } + + [Fact] + public void ShouldBuildApiTokenUsingPrefixedConfiguration() + { + + const string envVarName = $"test_{DaprDefaults.DaprApiTokenName}"; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + + try + { + const string prefix = "test_"; + + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } + + [Fact] + public void ShouldBuildApiTokenWithEnvVarWhenConfigurationNotAvailable() + { + const string envVarName = DaprDefaults.DaprApiTokenName; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + try + { + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } +} From 5f21620ecf675b3be8476607945cc161af045a79 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:27:26 +0200 Subject: [PATCH 3/7] cleanup: Removed Serilog nuget from Directory.Packages.props (#1376) * Removed Serilog nuget from Directory.Packages.props Signed-off-by: Manuel Menegazzo * Update Directory.Packages.props Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> --------- Signed-off-by: Manuel Menegazzo Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> --- Directory.Packages.props | 20 +++++++++---------- .../Dapr.E2E.Test.App.csproj | 3 +-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 842c82aa8..3c1459b5d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,11 +3,10 @@ true true - - + @@ -16,10 +15,10 @@ - + - + @@ -30,20 +29,19 @@ - + - + - + - - + - + - \ No newline at end of file + diff --git a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj index 3454ac25d..162a1306a 100644 --- a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj +++ b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj @@ -8,9 +8,8 @@ - - + From ee8be673374f7ff0c65b9ff4f506de874f6a2ad3 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:33:13 +0200 Subject: [PATCH 4/7] Removed sample folder (#1375) Signed-off-by: Manuel Menegazzo Co-authored-by: Whit Waldo --- all.sln | 7 +---- samples/Client/README.md | 56 ---------------------------------------- 2 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 samples/Client/README.md diff --git a/all.sln b/all.sln index 85ed848a4..1dd0ab3c5 100644 --- a/all.sln +++ b/all.sln @@ -1,5 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 +# 17 VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors", "src\Dapr.Actors\Dapr.Actors.csproj", "{C2DB4B64-B7C3-4FED-8753-C040F677C69A}" @@ -14,11 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Client", "src\Dapr.Cli EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.AspNetCore", "src\Dapr.AspNetCore\Dapr.AspNetCore.csproj", "{08D602F6-7C11-4653-B70B-B56333BF6FD2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B2DB41EE-45F5-447B-95E8-38E1E8B70C4E}" - ProjectSection(SolutionItems) = preProject - samples\.editorconfig = samples\.editorconfig - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DD020B34-460F-455F-8D17-CF4A949F100B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Client.Test", "test\Dapr.Client.Test\Dapr.Client.Test.csproj", "{383609C1-F43F-49EB-85E4-1964EE7F0F14}" diff --git a/samples/Client/README.md b/samples/Client/README.md deleted file mode 100644 index 2bb738d89..000000000 --- a/samples/Client/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Dapr Client examples - -The following examples will show you how to: - -- Invoke services -- Publish events -- Use the state store to get, set, and delete data - -## Prerequisites - -* [.NET 6+](https://dotnet.microsoft.com/download) -* [Dapr CLI](https://github.com/dapr/cli) -* [Dapr DotNet SDK](https://github.com/dapr/dotnet-sdk) - -## Running the Sample - -To run the sample locally run this command in DaprClient directory: -```sh -dapr run --app-id DaprClient -- dotnet run -``` - -Running the following command will output a list of the samples included. -```sh -dapr run --app-id DaprClient -- dotnet run -``` - -Press Ctrl+C to exit, and then run the command again and provide a sample number to run the samples. - -For example run this command to run the 0th sample from the list produced earlier. -```sh -dapr run --app-id DaprClient -- dotnet run 0 -``` - -Samples that use HTTP-based service invocation will require running the [RoutingService](../../AspNetCore/RoutingSample). - -Samples that use gRPC-based service invocation will require running [GrpcService](../../AspNetCore/GrpcServiceSample). - -## Invoking Services - -See: `InvokeServiceHttpClientExample.cs` for an example of using `HttpClient` to invoke another service through Dapr. - -See: `InvokeServiceHttpExample.cs` for an example using the `DaprClient` to invoke another service through Dapr. - -See: `InvokeServiceGrpcExample.cs` for an example using the `DaprClient` to invoke a service using gRPC through Dapr. - -## Publishing Pub/Sub Events - -See: `PublishEventExample.cs` for an example using the `DaprClient` to publish a pub/sub event. - -## Working with the State Store - -See: `StateStoreExample.cs` for an example of using `DaprClient` for basic state store operations like get, set, and delete. - -See: `StateStoreETagsExample.cs` for an example of using `DaprClient` for optimistic concurrency control with the state store. - -See: `StateStoreTransactionsExample.cs` for an example of using `DaprClient` for transactional state store operations that affect multiple keys. From 94b97e224f92ab4d4667390a00ef00d5c61d6747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A2mara?= <52082556+RafaelJCamara@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:27:39 +0200 Subject: [PATCH 5/7] Remove unused variables (#1314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove unused variables Signed-off-by: Rafael Camara Signed-off-by: Rafael Câmara <52082556+RafaelJCamara@users.noreply.github.com> Co-authored-by: Whit Waldo --- examples/Actor/ActorClient/Program.cs | 2 +- examples/AspNetCore/RoutingSample/Startup.cs | 1 - .../GeneratedActor/ActorClient/Program.cs | 2 +- .../WorkflowUnitTest/OrderProcessingTests.cs | 2 +- .../ActorClientGenerator.cs | 1 - src/Dapr.Actors/Runtime/ActorManager.cs | 3 +- .../HostingTests.cs | 2 +- .../Dapr.Actors.Test/ActorCodeBuilderTests.cs | 2 +- test/Dapr.Actors.Test/ActorIdTests.cs | 5 +- .../Runtime/ActorManagerTests.cs | 2 +- .../Runtime/ActorRuntimeOptionsTests.cs | 2 - .../Serialization/ActorIdJsonConverterTest.cs | 15 ++- .../ControllerIntegrationTest.cs | 3 - .../DaprClientBuilderTest.cs | 6 +- test/Dapr.Client.Test/DaprApiTokenTest.cs | 4 +- .../DaprClientTest.InvokeMethodGrpcAsync.cs | 11 +- .../DistributedLockApiTest.cs | 1 - .../InvocationHandlerTests.cs | 6 +- test/Dapr.Client.Test/InvokeBindingApiTest.cs | 2 +- test/Dapr.Client.Test/PublishEventApiTest.cs | 2 - test/Dapr.Client.Test/StateApiTest.cs | 23 ++-- test/Dapr.Client.Test/TryLockResponseTest.cs | 2 +- .../Clients/GeneratedClientTests.cs | 2 +- test/Dapr.E2E.Test.App/Startup.cs | 2 +- .../Actors/E2ETests.ExceptionTests.cs | 3 +- ...aprSecretStoreConfigurationProviderTest.cs | 102 +++++++++--------- 26 files changed, 94 insertions(+), 114 deletions(-) diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs index f6ca26f53..950869b2b 100644 --- a/examples/Actor/ActorClient/Program.cs +++ b/examples/Actor/ActorClient/Program.cs @@ -85,7 +85,7 @@ public static async Task Main(string[] args) var nonRemotingProxy = ActorProxy.Create(actorId, "DemoActor"); await nonRemotingProxy.InvokeMethodAsync("TestNoArgumentNoReturnType"); await nonRemotingProxy.InvokeMethodAsync("SaveData", data); - var res = await nonRemotingProxy.InvokeMethodAsync("GetData"); + await nonRemotingProxy.InvokeMethodAsync("GetData"); Console.WriteLine("Registering the timer and reminder"); await proxy.RegisterTimer(); diff --git a/examples/AspNetCore/RoutingSample/Startup.cs b/examples/AspNetCore/RoutingSample/Startup.cs index 29bb488fe..3d71e9e1f 100644 --- a/examples/AspNetCore/RoutingSample/Startup.cs +++ b/examples/AspNetCore/RoutingSample/Startup.cs @@ -221,7 +221,6 @@ await JsonSerializer.SerializeAsync(context.Response.Body, async Task ViewErrorMessage(HttpContext context) { - var client = context.RequestServices.GetRequiredService(); var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); logger.LogInformation("The amount cannot be negative: {0}", transaction.Amount); diff --git a/examples/GeneratedActor/ActorClient/Program.cs b/examples/GeneratedActor/ActorClient/Program.cs index 87f714907..0b858214a 100644 --- a/examples/GeneratedActor/ActorClient/Program.cs +++ b/examples/GeneratedActor/ActorClient/Program.cs @@ -23,7 +23,7 @@ var client = new ClientActorClient(proxy); -var state = await client.GetStateAsync(cancellationTokenSource.Token); +await client.GetStateAsync(cancellationTokenSource.Token); await client.SetStateAsync(new ClientState("Hello, World!"), cancellationTokenSource.Token); diff --git a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs index e38a0c940..3093e5c7a 100644 --- a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs +++ b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs @@ -64,7 +64,7 @@ public async Task TestInsufficientInventory() .Returns(Task.FromResult(inventoryResult)); // Run the workflow directly - OrderResult result = await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); + await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); // Verify that ReserveInventoryActivity was called with a specific input mockContext.Verify( diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 349d80188..f95fc4224 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -112,7 +112,6 @@ public void Execute(GeneratorExecutionContext context) { try { - var actorInterfaceTypeName = interfaceSymbol.Name; var fullyQualifiedActorInterfaceTypeName = interfaceSymbol.ToString(); var attributeData = interfaceSymbol.GetAttributes().Single(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); diff --git a/src/Dapr.Actors/Runtime/ActorManager.cs b/src/Dapr.Actors/Runtime/ActorManager.cs index 80049d65f..a641440cf 100644 --- a/src/Dapr.Actors/Runtime/ActorManager.cs +++ b/src/Dapr.Actors/Runtime/ActorManager.cs @@ -229,7 +229,6 @@ internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, Ca // Create a Func to be invoked by common method. async Task RequestFunc(Actor actor, CancellationToken ct) { - var actorTypeName = actor.Host.ActorTypeInfo.ActorTypeName; var actorType = actor.Host.ActorTypeInfo.ImplementationType; var methodInfo = actor.GetMethodInfoUsingReflection(actorType, timerData.Callback); @@ -241,7 +240,7 @@ async Task RequestFunc(Actor actor, CancellationToken ct) return default; } - var result = await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken); + await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken); } internal async Task ActivateActorAsync(ActorId actorId) diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs index 7f96c66e6..bf3757ce1 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs @@ -42,7 +42,7 @@ public void MapActorsHandlers_WithoutAddActors_Throws() // NOTE: in 3.1 TestServer.CreateClient triggers the failure, in 5.0 it's Host.Start using var host = CreateHost(); var server = host.GetTestServer(); - var client = server.CreateClient(); + server.CreateClient(); }); Assert.Equal( diff --git a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs index 6bb3c827d..1b4a3fb26 100644 --- a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs +++ b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs @@ -32,7 +32,7 @@ public class ActorCodeBuilderTests [Fact] public void TestBuildActorProxyGenerator() { - ActorProxyGenerator proxyGenerator = ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor)); + ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor)); } [Fact] diff --git a/test/Dapr.Actors.Test/ActorIdTests.cs b/test/Dapr.Actors.Test/ActorIdTests.cs index 668bd0fbf..c54f4c351 100644 --- a/test/Dapr.Actors.Test/ActorIdTests.cs +++ b/test/Dapr.Actors.Test/ActorIdTests.cs @@ -115,10 +115,7 @@ public class ActorIdTests [InlineData(" ")] public void Initialize_New_ActorId_Object_With_Null_Or_Whitespace_Id(string id) { - Assert.Throws(() => - { - ActorId actorId = new ActorId(id); - }); + Assert.Throws(() => new ActorId(id)); } [Theory] diff --git a/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs b/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs index 6b92c7e18..b27e9afe3 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs @@ -164,7 +164,7 @@ public async Task DeactivateActorAsync_ExceptionDuringDeactivation_ActorIsRemove var id = ActorId.CreateRandom(); await manager.ActivateActorAsync(id); - Assert.True(manager.TryGetActorAsync(id, out var actor)); + Assert.True(manager.TryGetActorAsync(id, out _)); await Assert.ThrowsAsync(async () => { diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs index 4fe991970..b68eda5ce 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs @@ -26,8 +26,6 @@ public void TestRegisterActor_SavesActivator() { var actorType = typeof(TestActor); var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); - var host = ActorHost.CreateForTest(); - var actor = new TestActor(host); var activator = Mock.Of(); diff --git a/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs b/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs index fe33eca54..9c4f22193 100644 --- a/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs +++ b/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs @@ -50,9 +50,9 @@ public void CanDeserializeActorId() { var id = ActorId.CreateRandom().GetId(); var document = $@" -{{ - ""actor"": ""{id}"" -}}"; + {{ + ""actor"": ""{id}"" + }}"; var deserialized = JsonSerializer.Deserialize(document); @@ -62,11 +62,10 @@ public void CanDeserializeActorId() [Fact] public void CanDeserializeNullActorId() { - var id = ActorId.CreateRandom().GetId(); - var document = $@" -{{ - ""actor"": null -}}"; + const string document = @" + { + ""actor"": null + }"; var deserialized = JsonSerializer.Deserialize(document); diff --git a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs index 364cd8448..7735ec4fb 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs @@ -67,7 +67,6 @@ public async Task ModelBinder_GetFromStateEntryWithKeyNotInStateStore_ReturnsNul using (var factory = new AppWebApplicationFactory()) { var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithoutstateentry/test"); var response = await httpClient.SendAsync(request); @@ -142,7 +141,6 @@ public async Task ModelBinder_GetFromStateEntryWithStateEntry_WithKeyNotInStateS using (var factory = new AppWebApplicationFactory()) { var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithstateentry/test"); var response = await httpClient.SendAsync(request); @@ -159,7 +157,6 @@ public async Task ModelBinder_CanGetOutOfTheWayWhenTheresNoBinding() using (var factory = new AppWebApplicationFactory()) { var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/echo-user?name=jimmy"); var response = await httpClient.SendAsync(request); diff --git a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs index 52d0b7000..bd807ebae 100644 --- a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs @@ -11,7 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; +using System; using System.Text.Json; using Dapr.Client; using Grpc.Core; @@ -44,7 +44,7 @@ public void DaprClientBuilder_UsesPropertyNameCaseHandlingAsSpecified() public void DaprClientBuilder_UsesThrowOperationCanceledOnCancellation_ByDefault() { var builder = new DaprClientBuilder(); - var daprClient = builder.Build(); + Assert.True(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); } @@ -52,7 +52,7 @@ public void DaprClientBuilder_UsesThrowOperationCanceledOnCancellation_ByDefault public void DaprClientBuilder_DoesNotOverrideUserGrpcChannelOptions() { var builder = new DaprClientBuilder(); - var daprClient = builder.UseGrpcChannelOptions(new GrpcChannelOptions()).Build(); + builder.UseGrpcChannelOptions(new GrpcChannelOptions()).Build(); Assert.False(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); } diff --git a/test/Dapr.Client.Test/DaprApiTokenTest.cs b/test/Dapr.Client.Test/DaprApiTokenTest.cs index 3726e0321..cf9f422bc 100644 --- a/test/Dapr.Client.Test/DaprApiTokenTest.cs +++ b/test/Dapr.Client.Test/DaprApiTokenTest.cs @@ -35,7 +35,7 @@ public async Task DaprCall_WithApiTokenSet() request.Dismiss(); // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); + await request.GetRequestEnvelopeAsync(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); headerValues.Count().Should().Be(1); @@ -56,7 +56,7 @@ public async Task DaprCall_WithoutApiToken() request.Dismiss(); // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); + await request.GetRequestEnvelopeAsync(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); headerValues.Should().BeNull(); diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs index 65b9b1e7d..6ccbbe4c4 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs @@ -88,8 +88,7 @@ public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeAndData_Thr Data = Any.Pack(data), }; - var response = - client.Call() + await client.Call() .SetResponse(invokeResponse) .Build(); @@ -153,8 +152,7 @@ public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeNoData_Thro Data = Any.Pack(data), }; - var response = - client.Call() + await client.Call() .SetResponse(invokeResponse) .Build(); @@ -210,8 +208,7 @@ public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData_T Data = Any.Pack(data), }; - var response = - client.Call() + await client.Call() .SetResponse(invokeResponse) .Build(); @@ -294,7 +291,7 @@ public async Task InvokeMethodGrpcAsync_WithReturnTypeAndData() // Validate Response var invokedResponse = await request.CompleteWithMessageAsync(response); - invokeResponse.Name.Should().Be(invokeResponse.Name); + invokedResponse.Name.Should().Be(invokeResponse.Name); } [Fact] diff --git a/test/Dapr.Client.Test/DistributedLockApiTest.cs b/test/Dapr.Client.Test/DistributedLockApiTest.cs index def7d8f8f..a030586c5 100644 --- a/test/Dapr.Client.Test/DistributedLockApiTest.cs +++ b/test/Dapr.Client.Test/DistributedLockApiTest.cs @@ -60,7 +60,6 @@ public async Task TryLockAsync_WithAllValues_ArgumentVerifierException() string lockOwner = "owner1"; Int32 expiryInSeconds = 0; // Get response and validate - var invokeResponse = new ArgumentException(); await Assert.ThrowsAsync(async () => await client.Lock(storeName, resourceId, lockOwner, expiryInSeconds)); } diff --git a/test/Dapr.Client.Test/InvocationHandlerTests.cs b/test/Dapr.Client.Test/InvocationHandlerTests.cs index 3dac84113..b171ca399 100644 --- a/test/Dapr.Client.Test/InvocationHandlerTests.cs +++ b/test/Dapr.Client.Test/InvocationHandlerTests.cs @@ -156,7 +156,7 @@ public async Task SendAsync_RewritesUri() }; var request = new HttpRequestMessage(HttpMethod.Post, uri); - var response = await CallSendAsync(handler, request); + await CallSendAsync(handler, request); Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString); Assert.Null(capture.DaprApiToken); @@ -181,7 +181,7 @@ public async Task SendAsync_RewritesUri_AndAppId() }; var request = new HttpRequestMessage(HttpMethod.Post, uri); - var response = await CallSendAsync(handler, request); + await CallSendAsync(handler, request); Assert.Equal("https://localhost:5000/v1.0/invoke/Bank/method/accounts/17?", capture.RequestUri?.OriginalString); Assert.Null(capture.DaprApiToken); @@ -205,7 +205,7 @@ public async Task SendAsync_RewritesUri_AndAddsApiToken() }; var request = new HttpRequestMessage(HttpMethod.Post, uri); - var response = await CallSendAsync(handler, request); + 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); diff --git a/test/Dapr.Client.Test/InvokeBindingApiTest.cs b/test/Dapr.Client.Test/InvokeBindingApiTest.cs index 30c9d48ba..c2ada7a11 100644 --- a/test/Dapr.Client.Test/InvokeBindingApiTest.cs +++ b/test/Dapr.Client.Test/InvokeBindingApiTest.cs @@ -209,7 +209,7 @@ public async Task InvokeBindingAsync_WrapsJsonException() return await daprClient.InvokeBindingAsync("test", "test", new InvokeRequest() { RequestParameter = "Hello " }); }); - var envelope = await request.GetRequestEnvelopeAsync(); + await request.GetRequestEnvelopeAsync(); var ex = await Assert.ThrowsAsync(async () => { await request.CompleteWithMessageAsync(response); diff --git a/test/Dapr.Client.Test/PublishEventApiTest.cs b/test/Dapr.Client.Test/PublishEventApiTest.cs index 77d6ee905..e3e21dd2b 100644 --- a/test/Dapr.Client.Test/PublishEventApiTest.cs +++ b/test/Dapr.Client.Test/PublishEventApiTest.cs @@ -142,7 +142,6 @@ public async Task PublishEventAsync_CanPublishTopicWithNoContent() request.Dismiss(); var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); envelope.PubsubName.Should().Be(TestPubsubName); envelope.Topic.Should().Be("test"); @@ -214,7 +213,6 @@ public async Task PublishEventAsync_CanPublishCloudEventWithNoContent() { await using var client = TestClient.CreateForDaprClient(); - var publishData = new PublishData() { PublishObjectParameter = "testparam" }; var cloudEvent = new CloudEvent { Source = new Uri("urn:testsource"), diff --git a/test/Dapr.Client.Test/StateApiTest.cs b/test/Dapr.Client.Test/StateApiTest.cs index 0684a8db0..b240605a1 100644 --- a/test/Dapr.Client.Test/StateApiTest.cs +++ b/test/Dapr.Client.Test/StateApiTest.cs @@ -839,8 +839,7 @@ public async Task TrySaveStateAsync_ValidateNonETagErrorThrowsException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi().Build(); var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); @@ -861,8 +860,8 @@ public async Task TrySaveStateAsync_ValidateETagRelatedExceptionReturnsFalse() { var client = new MockClient(); - var response = 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 @@ -879,8 +878,8 @@ public async Task TrySaveStateAsync_NullEtagThrowsArgumentException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null)) .Should().ThrowAsync(); @@ -907,8 +906,8 @@ public async Task TryDeleteStateAsync_ValidateNonETagErrorThrowsException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); @@ -929,8 +928,8 @@ public async Task TryDeleteStateAsync_NullEtagThrowsArgumentException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); await FluentActions.Awaiting(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null)) .Should().ThrowAsync(); @@ -957,8 +956,8 @@ public async Task TryDeleteStateAsync_ValidateETagRelatedExceptionReturnsFalse() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + 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 diff --git a/test/Dapr.Client.Test/TryLockResponseTest.cs b/test/Dapr.Client.Test/TryLockResponseTest.cs index f94f949db..3420dc233 100644 --- a/test/Dapr.Client.Test/TryLockResponseTest.cs +++ b/test/Dapr.Client.Test/TryLockResponseTest.cs @@ -47,7 +47,7 @@ public async Task TryLockAsync_WithAllValues_ValidateRequest() Success = true }; - var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); + await request.CompleteWithMessageAsync(invokeResponse); //testing unlocking diff --git a/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs b/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs index 6079b5df7..a089a2e82 100644 --- a/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs +++ b/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs @@ -100,7 +100,7 @@ public async Task TestGeneratedClientAsync() var client = new ClientActorClient(actorProxy); - var result = await client.GetStateAsync(cancellationTokenSource.Token); + await client.GetStateAsync(cancellationTokenSource.Token); await client.SetStateAsync(new ClientState("updated state"), cancellationTokenSource.Token); } diff --git a/test/Dapr.E2E.Test.App/Startup.cs b/test/Dapr.E2E.Test.App/Startup.cs index bfca60f91..7e7484d54 100644 --- a/test/Dapr.E2E.Test.App/Startup.cs +++ b/test/Dapr.E2E.Test.App/Startup.cs @@ -120,7 +120,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { var logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); - var loggerFactory = LoggerFactory.Create(builder => + LoggerFactory.Create(builder => { builder.AddSerilog(logger); }); diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs index 986a2c4f0..fc036d15d 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs @@ -25,8 +25,7 @@ public partial class E2ETests : IAsyncLifetime public async Task ActorCanProvideExceptionDetails() { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var actorIds = new ActorId(Guid.NewGuid().ToString()); - + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ExceptionActor"); await WaitForActorRuntimeAsync(proxy, cts.Token); ActorMethodInvocationException ex = await Assert.ThrowsAsync(async () => await proxy.ExceptionExample()); diff --git a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs index 74b66c3cb..fd323f989 100644 --- a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs +++ b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs @@ -41,11 +41,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithoutStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(null, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(null, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) + .Build(); + }); Assert.Contains("store", ex.Message); } @@ -58,11 +58,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithEmptyStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(string.Empty, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(string.Empty, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) + .Build(); + }); Assert.Contains("The value cannot be null or empty", ex.Message); } @@ -75,11 +75,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithoutSecretDescriptors_Reports .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", (DaprSecretDescriptor[])null, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", (DaprSecretDescriptor[])null, daprClient) + .Build(); + }); Assert.Contains("secretDescriptors", ex.Message); } @@ -88,11 +88,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithoutSecretDescriptors_Reports public void AddDaprSecretStore_UsingDescriptors_WithoutClient_ReportsError() { var ex = Assert.Throws(() => - { - var config = 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); } @@ -105,11 +105,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithZeroSecretDescriptors_Report .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { }, daprClient) + .Build(); + }); Assert.Contains("No secret descriptor was provided", ex.Message); } @@ -132,11 +132,11 @@ public void AddDaprSecretStore_UsingDescriptors_DuplicateSecret_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) + .Build(); + }); Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); } @@ -310,7 +310,7 @@ public void LoadSecrets_FromSecretStoreRequiredAndDoesNotExist_ShouldThrowExcept var ex = Assert.Throws(() => { - var config = CreateBuilder() + CreateBuilder() .AddDaprSecretStore(storeName, secretDescriptors, daprClient) .Build(); }); @@ -327,11 +327,11 @@ public void AddDaprSecretStore_WithoutStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(null, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(null, daprClient) + .Build(); + }); Assert.Contains("store", ex.Message); } @@ -344,11 +344,11 @@ public void AddDaprSecretStore_WithEmptyStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(string.Empty, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(string.Empty, daprClient) + .Build(); + }); Assert.Contains("The value cannot be null or empty", ex.Message); } @@ -357,11 +357,11 @@ public void AddDaprSecretStore_WithEmptyStore_ReportsError() public void AddDaprSecretStore_WithoutClient_ReportsError() { var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", null) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", null) + .Build(); + }); Assert.Contains("client", ex.Message); } @@ -384,11 +384,11 @@ public void AddDaprSecretStore_DuplicateSecret_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", daprClient) + .Build(); + }); Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); } From c61b15d5b1558852765d5e625be4b27d14a13fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A2mara?= <52082556+RafaelJCamara@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:00:09 +0200 Subject: [PATCH 6/7] Remove unused using statements. (#1313) Signed-off-by: Rafael Camara --- .../AspNetCore/GrpcServiceSample/Program.cs | 5 --- .../Program.cs | 1 - .../Controllers/ConfigurationController.cs | 4 +-- examples/Client/ConfigurationApi/Program.cs | 1 - examples/Client/ConfigurationApi/Startup.cs | 5 +-- .../Controllers/BindingController.cs | 5 ++- .../Client/DistributedLock/Model/StateData.cs | 3 +- examples/Client/DistributedLock/Program.cs | 5 --- .../InvokeServiceHttpClientExample.cs | 1 - .../Activities/ProcessPaymentActivity.cs | 3 +- .../Communication/ActorInvokeException.cs | 5 --- .../Communication/ActorInvokeExceptionData.cs | 3 -- ...torMessageBodyJsonSerializationProvider.cs | 1 - src/Dapr.Actors/Runtime/ActorStateManager.cs | 1 - .../Runtime/DefaultActorActivatorFactory.cs | 3 -- .../Runtime/DefaultActorTimerManager.cs | 1 - src/Dapr.Actors/Runtime/IActorStateManager.cs | 1 - .../DaprAuthenticationBuilderExtensions.cs | 1 - .../DaprMvcBuilderExtensions.cs | 2 -- src/Dapr.AspNetCore/FromStateAttribute.cs | 1 - src/Dapr.Client/InvocationInterceptor.cs | 2 -- src/Dapr.Client/StartWorkflowResponse.cs | 3 -- src/Dapr.Client/StateOptions.cs | 4 --- src/Dapr.Client/StateTransactionRequest.cs | 2 -- .../SubscribeConfigurationResponse.cs | 3 +- src/Dapr.Client/UnlockResponse.cs | 2 -- .../UnsubscribeConfigurationResponse.cs | 3 +- .../DaprConfigurationStoreSource.cs | 1 - .../DaprSecretStoreConfigurationExtensions.cs | 1 - .../DaprSecretStoreConfigurationSource.cs | 1 - src/Dapr.Workflow/WorkflowLoggingService.cs | 1 - .../AppWebApplicationFactory.cs | 1 - .../DependencyInjectionActorActivatorTests.cs | 2 -- .../Dapr.Actors.Test/ActorCodeBuilderTests.cs | 1 - .../ActorMethodInvocationExceptionTests.cs | 1 - .../ActorProxyOptionsTests.cs | 4 --- .../Dapr.Actors.Test/ActorStateManagerTest.cs | 7 ----- test/Dapr.Actors.Test/ActorUnitTestTests.cs | 2 -- .../DaprFormatTimeSpanTests.cs | 2 -- .../DaprHttpInteractorTest.cs | 2 -- .../Dapr.Actors.Test/DaprStateProviderTest.cs | 7 ----- .../Runtime/ActorRuntimeTests.cs | 1 - .../Runtime/DefaultActorActivatorTests.cs | 2 -- .../ActorIdDataContractSerializationTest.cs | 4 --- .../TimerInfoJsonConverterTest.cs | 1 - .../StateTestClient.cs | 2 -- .../CloudEventsMiddlewareTest.cs | 1 - .../DaprClientBuilderTest.cs | 1 - .../StateEntryApplicationModelProviderTest.cs | 8 ----- test/Dapr.Client.Test/ConfigurationApiTest.cs | 1 - .../DaprClientTest.InvokeMethodAsync.cs | 3 +- test/Dapr.Client.Test/PublishEventApiTest.cs | 1 - test/Dapr.Client.Test/StateApiTest.cs | 2 -- test/Dapr.Client.Test/TypeConvertersTest.cs | 1 - test/Dapr.E2E.Test.App.Grpc/Startup.cs | 31 ++++++++----------- test/Dapr.E2E.Test.App/Actors/StateActor.cs | 1 - .../Controllers/TestController.cs | 2 -- test/Dapr.E2E.Test.App/Startup.cs | 2 -- .../Actors/E2ETests.StateTests.cs | 1 - .../E2ETests.ServiceInvocationTests.cs | 4 --- test/Shared/GrpcUtils.cs | 1 - 61 files changed, 22 insertions(+), 152 deletions(-) diff --git a/examples/AspNetCore/GrpcServiceSample/Program.cs b/examples/AspNetCore/GrpcServiceSample/Program.cs index cd8486447..dbec5ba2c 100644 --- a/examples/AspNetCore/GrpcServiceSample/Program.cs +++ b/examples/AspNetCore/GrpcServiceSample/Program.cs @@ -11,11 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Hosting; diff --git a/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs b/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs index 7afb17436..658d6d163 100644 --- a/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs +++ b/examples/AspNetCore/SecretStoreConfigurationProviderSample/Program.cs @@ -18,7 +18,6 @@ using Dapr.Client; using Dapr.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; - using System.Collections.Generic; using System; /// diff --git a/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs b/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs index 55bf6df53..9ceb60c0b 100644 --- a/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs +++ b/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading; +using System.Collections.Generic; using System.Threading.Tasks; using ControllerSample; using Dapr; diff --git a/examples/Client/ConfigurationApi/Program.cs b/examples/Client/ConfigurationApi/Program.cs index f5218602d..22f12cfae 100644 --- a/examples/Client/ConfigurationApi/Program.cs +++ b/examples/Client/ConfigurationApi/Program.cs @@ -4,7 +4,6 @@ using Dapr.Client; using Dapr.Extensions.Configuration; using System.Collections.Generic; -using System.Threading; namespace ConfigurationApi { diff --git a/examples/Client/ConfigurationApi/Startup.cs b/examples/Client/ConfigurationApi/Startup.cs index db5b921c9..b858b810c 100644 --- a/examples/Client/ConfigurationApi/Startup.cs +++ b/examples/Client/ConfigurationApi/Startup.cs @@ -1,11 +1,8 @@ -using System; -using Dapr.AspNetCore; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Primitives; namespace ConfigurationApi { diff --git a/examples/Client/DistributedLock/Controllers/BindingController.cs b/examples/Client/DistributedLock/Controllers/BindingController.cs index 1edc6aafe..aa4dd1f52 100644 --- a/examples/Client/DistributedLock/Controllers/BindingController.cs +++ b/examples/Client/DistributedLock/Controllers/BindingController.cs @@ -1,6 +1,5 @@ -using System; -using System.Linq; -using System.Text; +using System; +using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Dapr; diff --git a/examples/Client/DistributedLock/Model/StateData.cs b/examples/Client/DistributedLock/Model/StateData.cs index d5c5f6e9a..0ad5d2fd9 100644 --- a/examples/Client/DistributedLock/Model/StateData.cs +++ b/examples/Client/DistributedLock/Model/StateData.cs @@ -1,5 +1,4 @@ -using System; -namespace DistributedLock.Model +namespace DistributedLock.Model { #nullable enable public class StateData diff --git a/examples/Client/DistributedLock/Program.cs b/examples/Client/DistributedLock/Program.cs index ba4d670a7..3080b5b1e 100644 --- a/examples/Client/DistributedLock/Program.cs +++ b/examples/Client/DistributedLock/Program.cs @@ -1,11 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; namespace DistributedLock { diff --git a/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs b/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs index fc1475b8e..72d68096d 100644 --- a/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs +++ b/examples/Client/ServiceInvocation/InvokeServiceHttpClientExample.cs @@ -13,7 +13,6 @@ using System; using System.Net.Http.Json; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Dapr.Client; diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs index 1ddb51bbf..4ca39f0ae 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs @@ -1,5 +1,4 @@ -using Dapr.Client; -using Dapr.Workflow; +using Dapr.Workflow; using Microsoft.Extensions.Logging; namespace WorkflowConsoleApp.Activities diff --git a/src/Dapr.Actors/Communication/ActorInvokeException.cs b/src/Dapr.Actors/Communication/ActorInvokeException.cs index ac4ea63df..d58ceace8 100644 --- a/src/Dapr.Actors/Communication/ActorInvokeException.cs +++ b/src/Dapr.Actors/Communication/ActorInvokeException.cs @@ -14,12 +14,7 @@ namespace Dapr.Actors { using System; - using System.Globalization; using System.IO; - using System.Runtime.Serialization; - using System.Text; - using System.Xml; - using Dapr.Actors.Resources; using Microsoft.Extensions.Logging; /// diff --git a/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs b/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs index b9f1f4d86..6103c79c4 100644 --- a/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs +++ b/src/Dapr.Actors/Communication/ActorInvokeExceptionData.cs @@ -13,12 +13,9 @@ namespace Dapr.Actors { - using System; using System.IO; using System.Runtime.Serialization; using System.Xml; - using Dapr.Actors; - using Microsoft.Extensions.Logging; [DataContract(Name = "ActorInvokeExceptionData", Namespace = Constants.Namespace)] internal class ActorInvokeExceptionData diff --git a/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs b/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs index fd35db8e1..062d3c742 100644 --- a/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs +++ b/src/Dapr.Actors/Communication/ActorMessageBodyJsonSerializationProvider.cs @@ -18,7 +18,6 @@ namespace Dapr.Actors.Communication using System.IO; using System.Text.Json; using System.Threading.Tasks; - using System.Xml; /// /// This is the implmentation for used by remoting service and client during diff --git a/src/Dapr.Actors/Runtime/ActorStateManager.cs b/src/Dapr.Actors/Runtime/ActorStateManager.cs index 111bb80f4..31ada4433 100644 --- a/src/Dapr.Actors/Runtime/ActorStateManager.cs +++ b/src/Dapr.Actors/Runtime/ActorStateManager.cs @@ -12,7 +12,6 @@ // ------------------------------------------------------------------------ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Threading; diff --git a/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs b/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs index c927ebe61..43839c64d 100644 --- a/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs +++ b/src/Dapr.Actors/Runtime/DefaultActorActivatorFactory.cs @@ -11,9 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Threading.Tasks; - namespace Dapr.Actors.Runtime { /// diff --git a/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs b/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs index b42b432a1..66f94f08e 100644 --- a/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs +++ b/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs @@ -15,7 +15,6 @@ using System.Text.Json; using System.Threading.Tasks; using System.IO; -using System.Text; namespace Dapr.Actors.Runtime { diff --git a/src/Dapr.Actors/Runtime/IActorStateManager.cs b/src/Dapr.Actors/Runtime/IActorStateManager.cs index b85fa2a06..8795e28e5 100644 --- a/src/Dapr.Actors/Runtime/IActorStateManager.cs +++ b/src/Dapr.Actors/Runtime/IActorStateManager.cs @@ -17,7 +17,6 @@ namespace Dapr.Actors.Runtime using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; - using Dapr.Actors.Communication; /// /// Represents an interface that exposes methods to manage state of an . diff --git a/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs index 0c37dbc8f..e55b2916e 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationBuilderExtensions.cs @@ -12,7 +12,6 @@ // ------------------------------------------------------------------------ using System; -using Dapr; using Dapr.AspNetCore; namespace Microsoft.AspNetCore.Authentication diff --git a/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs index 6195b9c30..6209fea5a 100644 --- a/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprMvcBuilderExtensions.cs @@ -15,8 +15,6 @@ namespace Microsoft.Extensions.DependencyInjection { using System; using System.Linq; - using System.Text.Json; - using Dapr; using Dapr.AspNetCore; using Dapr.Client; using Microsoft.AspNetCore.Mvc; diff --git a/src/Dapr.AspNetCore/FromStateAttribute.cs b/src/Dapr.AspNetCore/FromStateAttribute.cs index 164d7ef09..16299ba07 100644 --- a/src/Dapr.AspNetCore/FromStateAttribute.cs +++ b/src/Dapr.AspNetCore/FromStateAttribute.cs @@ -14,7 +14,6 @@ namespace Microsoft.AspNetCore.Mvc { using System; - using System.ComponentModel; using Dapr; using Microsoft.AspNetCore.Mvc.ModelBinding; diff --git a/src/Dapr.Client/InvocationInterceptor.cs b/src/Dapr.Client/InvocationInterceptor.cs index bf9c8f482..b4ecc4b51 100644 --- a/src/Dapr.Client/InvocationInterceptor.cs +++ b/src/Dapr.Client/InvocationInterceptor.cs @@ -11,8 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Threading.Tasks; using Grpc.Core; using Grpc.Core.Interceptors; diff --git a/src/Dapr.Client/StartWorkflowResponse.cs b/src/Dapr.Client/StartWorkflowResponse.cs index 8442fd40e..a927116cb 100644 --- a/src/Dapr.Client/StartWorkflowResponse.cs +++ b/src/Dapr.Client/StartWorkflowResponse.cs @@ -11,9 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Collections.Generic; - namespace Dapr.Client { /// diff --git a/src/Dapr.Client/StateOptions.cs b/src/Dapr.Client/StateOptions.cs index 04c878fc0..fa6f2739c 100644 --- a/src/Dapr.Client/StateOptions.cs +++ b/src/Dapr.Client/StateOptions.cs @@ -13,10 +13,6 @@ namespace Dapr.Client { - using System; - using System.Collections.Generic; - using System.Text; - /// /// Options when perfroming state operations with Dapr. /// diff --git a/src/Dapr.Client/StateTransactionRequest.cs b/src/Dapr.Client/StateTransactionRequest.cs index 1e47d9344..756f3069f 100644 --- a/src/Dapr.Client/StateTransactionRequest.cs +++ b/src/Dapr.Client/StateTransactionRequest.cs @@ -14,8 +14,6 @@ namespace Dapr.Client { using System.Collections.Generic; - using System.Threading; - using Dapr.Client; /// /// Represents a single request in in a StateTransaction. diff --git a/src/Dapr.Client/SubscribeConfigurationResponse.cs b/src/Dapr.Client/SubscribeConfigurationResponse.cs index b371bd21c..bea992890 100644 --- a/src/Dapr.Client/SubscribeConfigurationResponse.cs +++ b/src/Dapr.Client/SubscribeConfigurationResponse.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace Dapr.Client { diff --git a/src/Dapr.Client/UnlockResponse.cs b/src/Dapr.Client/UnlockResponse.cs index 62480c8f6..68cc40670 100644 --- a/src/Dapr.Client/UnlockResponse.cs +++ b/src/Dapr.Client/UnlockResponse.cs @@ -11,8 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System.Collections.Generic; - namespace Dapr.Client { /// diff --git a/src/Dapr.Client/UnsubscribeConfigurationResponse.cs b/src/Dapr.Client/UnsubscribeConfigurationResponse.cs index 14c973df5..8208efb39 100644 --- a/src/Dapr.Client/UnsubscribeConfigurationResponse.cs +++ b/src/Dapr.Client/UnsubscribeConfigurationResponse.cs @@ -1,5 +1,4 @@ -using System; -namespace Dapr.Client +namespace Dapr.Client { /// /// Response from an Unsubscribe Configuration call. diff --git a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs index 2007ebc5b..e7c2bf34a 100644 --- a/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs +++ b/src/Dapr.Extensions.Configuration/DaprConfigurationStoreSource.cs @@ -13,7 +13,6 @@ using System; using System.Collections.Generic; -using System.Threading; using Dapr.Client; using Microsoft.Extensions.Configuration; diff --git a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs index c3d53e072..15ae6ab80 100644 --- a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs +++ b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationExtensions.cs @@ -17,7 +17,6 @@ using Microsoft.Extensions.Configuration; using Dapr.Extensions.Configuration.DaprSecretStore; using System.Linq; -using System.Threading; namespace Dapr.Extensions.Configuration { diff --git a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs index 282dc2765..eee3d0b2a 100644 --- a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs +++ b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationSource.cs @@ -13,7 +13,6 @@ using System; using System.Collections.Generic; -using System.Threading; using Dapr.Client; using Microsoft.Extensions.Configuration; diff --git a/src/Dapr.Workflow/WorkflowLoggingService.cs b/src/Dapr.Workflow/WorkflowLoggingService.cs index 482d95b97..331156f3e 100644 --- a/src/Dapr.Workflow/WorkflowLoggingService.cs +++ b/src/Dapr.Workflow/WorkflowLoggingService.cs @@ -13,7 +13,6 @@ namespace Dapr.Workflow { - using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs index 60e91b9e3..de27c9300 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/AppWebApplicationFactory.cs @@ -14,7 +14,6 @@ namespace Dapr.Actors.AspNetCore.IntegrationTest { using Dapr.Actors.AspNetCore.IntegrationTest.App; - using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; diff --git a/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs b/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs index aca679993..9c3b55365 100644 --- a/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs +++ b/test/Dapr.Actors.AspNetCore.Test/Runtime/DependencyInjectionActorActivatorTests.cs @@ -13,9 +13,7 @@ using System; using System.Threading.Tasks; -using Dapr.Actors.Client; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Dapr.Actors.Runtime diff --git a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs index 1b4a3fb26..2f2bb67db 100644 --- a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs +++ b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs @@ -13,7 +13,6 @@ namespace Dapr.Actors.Test { - using System.Threading; using System.Threading.Tasks; using Dapr.Actors.Builder; using Dapr.Actors.Communication; diff --git a/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs b/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs index 5b1e0f526..1311a9394 100644 --- a/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs +++ b/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs @@ -15,7 +15,6 @@ namespace Dapr.Actors.Test { using System; using System.IO; - using Dapr.Actors.Communication; using FluentAssertions; using Xunit; diff --git a/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs b/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs index d9ec2cc32..9fab1c32c 100644 --- a/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs +++ b/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs @@ -14,10 +14,6 @@ namespace Dapr.Actors.Client { using System; - using System.Text.Json; - using Dapr.Actors.Builder; - using Dapr.Actors.Client; - using Dapr.Actors.Test; using FluentAssertions; using Xunit; diff --git a/test/Dapr.Actors.Test/ActorStateManagerTest.cs b/test/Dapr.Actors.Test/ActorStateManagerTest.cs index a6517a6b4..a4e0e4140 100644 --- a/test/Dapr.Actors.Test/ActorStateManagerTest.cs +++ b/test/Dapr.Actors.Test/ActorStateManagerTest.cs @@ -14,17 +14,10 @@ namespace Dapr.Actors.Test { using System; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Security; - using System.Security.Authentication; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; - using FluentAssertions; using Xunit; using Dapr.Actors.Communication; using Dapr.Actors.Runtime; diff --git a/test/Dapr.Actors.Test/ActorUnitTestTests.cs b/test/Dapr.Actors.Test/ActorUnitTestTests.cs index baa52c568..891011de9 100644 --- a/test/Dapr.Actors.Test/ActorUnitTestTests.cs +++ b/test/Dapr.Actors.Test/ActorUnitTestTests.cs @@ -15,9 +15,7 @@ using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; -using Dapr.Actors.Client; using Dapr.Actors.Runtime; -using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; diff --git a/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs b/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs index 9d5710dfc..9d44e7550 100644 --- a/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs +++ b/test/Dapr.Actors.Test/DaprFormatTimeSpanTests.cs @@ -17,8 +17,6 @@ namespace Dapr.Actors.Test using System; using System.Collections.Generic; using System.IO; - using System.Text; - using System.Text.Json; using System.Threading.Tasks; using Dapr.Actors.Runtime; using Newtonsoft.Json; diff --git a/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs b/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs index 21c142267..9c42b8fc0 100644 --- a/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs +++ b/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs @@ -18,13 +18,11 @@ namespace Dapr.Actors.Test using System.Linq; using System.Net; using System.Net.Http; - using System.Security; using System.Security.Authentication; using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; using Xunit; - using Dapr.Actors.Communication; /// /// Contains tests for DaprHttpInteractor. diff --git a/test/Dapr.Actors.Test/DaprStateProviderTest.cs b/test/Dapr.Actors.Test/DaprStateProviderTest.cs index 63be89e95..948b14b46 100644 --- a/test/Dapr.Actors.Test/DaprStateProviderTest.cs +++ b/test/Dapr.Actors.Test/DaprStateProviderTest.cs @@ -14,17 +14,10 @@ namespace Dapr.Actors.Test { using System; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Security; - using System.Security.Authentication; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; - using FluentAssertions; using Xunit; using Dapr.Actors.Communication; using Dapr.Actors.Runtime; diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs index c74d0b754..6ebfe1bb2 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs @@ -15,7 +15,6 @@ namespace Dapr.Actors.Test { using System; using System.Buffers; - using System.Collections.Generic; using System.Linq; using System.IO; using System.Text; diff --git a/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs b/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs index f5ee9ae53..87045b295 100644 --- a/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs +++ b/test/Dapr.Actors.Test/Runtime/DefaultActorActivatorTests.cs @@ -13,8 +13,6 @@ using System; using System.Threading.Tasks; -using Dapr.Actors.Client; -using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Dapr.Actors.Runtime diff --git a/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs b/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs index c7b879d70..dfaccbc19 100644 --- a/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs +++ b/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs @@ -11,13 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; using System.IO; using System.Runtime.Serialization; -using System.Text.Json; -using System.Text.Json.Serialization; using System.Xml; -using System.Xml.Linq; using FluentAssertions; using Xunit; diff --git a/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs b/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs index 5799c172e..264fcb8a0 100644 --- a/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs +++ b/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs @@ -13,7 +13,6 @@ using System; using System.Text.Json; -using System.Text.Json.Serialization; using Xunit; #pragma warning disable 0618 diff --git a/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs b/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs index f9ad63b7b..b4940835c 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/StateTestClient.cs @@ -16,10 +16,8 @@ namespace Dapr.Client using System; using System.Collections.Generic; using System.Net.Http; - using System.Text.Json; using System.Threading; using System.Threading.Tasks; - using Dapr.Client; using Grpc.Net.Client; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; diff --git a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs index c8a5ff402..9c1f1e005 100644 --- a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs +++ b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs @@ -20,7 +20,6 @@ namespace Dapr.AspNetCore.Test using System.Threading.Tasks; using FluentAssertions; using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Xunit; diff --git a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs index bd807ebae..45cbcc152 100644 --- a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs @@ -14,7 +14,6 @@ using System; using System.Text.Json; using Dapr.Client; -using Grpc.Core; using Grpc.Net.Client; using Xunit; diff --git a/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs b/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs index d6b966c97..06572caa5 100644 --- a/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs +++ b/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs @@ -17,19 +17,11 @@ namespace Dapr.AspNetCore.Test using System.Collections.Generic; using System.Linq; using System.Reflection; - using System.Text.Json; - using System.Threading.Tasks; using Dapr.AspNetCore.Resources; - using Dapr.Client; - using Dapr.Client.Autogen.Grpc.v1; using FluentAssertions; - using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ModelBinding; - using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; - using Microsoft.AspNetCore.Mvc.Routing; - using Microsoft.Extensions.DependencyInjection; using Xunit; public class StateEntryApplicationModelProviderTest diff --git a/test/Dapr.Client.Test/ConfigurationApiTest.cs b/test/Dapr.Client.Test/ConfigurationApiTest.cs index 2e96582bd..095024933 100644 --- a/test/Dapr.Client.Test/ConfigurationApiTest.cs +++ b/test/Dapr.Client.Test/ConfigurationApiTest.cs @@ -16,7 +16,6 @@ using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Xunit; using FluentAssertions; -using System; namespace Dapr.Client.Test { diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs index 3359c3b48..58663227c 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs @@ -11,13 +11,13 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; namespace Dapr.Client.Test { using System; - using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Net.Http.Json; @@ -26,7 +26,6 @@ namespace Dapr.Client.Test using System.Threading; using System.Threading.Tasks; using Dapr.Client; - using FluentAssertions; using Xunit; // Most of the InvokeMethodAsync functionality on DaprClient is non-abstract methods that diff --git a/test/Dapr.Client.Test/PublishEventApiTest.cs b/test/Dapr.Client.Test/PublishEventApiTest.cs index e3e21dd2b..6ba774e52 100644 --- a/test/Dapr.Client.Test/PublishEventApiTest.cs +++ b/test/Dapr.Client.Test/PublishEventApiTest.cs @@ -11,7 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System.Collections.Immutable; using System.Linq; using System.Net.Http; using System.Text.Json.Serialization; diff --git a/test/Dapr.Client.Test/StateApiTest.cs b/test/Dapr.Client.Test/StateApiTest.cs index b240605a1..f6ecb5d80 100644 --- a/test/Dapr.Client.Test/StateApiTest.cs +++ b/test/Dapr.Client.Test/StateApiTest.cs @@ -22,14 +22,12 @@ namespace Dapr.Client.Test using FluentAssertions; using Google.Protobuf; using Grpc.Core; - using Grpc.Net.Client; using Moq; using StateConsistency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConsistency; using StateConcurrency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConcurrency; using Xunit; using System.Threading; using System.Net.Http; - using System.Text; public class StateApiTest { diff --git a/test/Dapr.Client.Test/TypeConvertersTest.cs b/test/Dapr.Client.Test/TypeConvertersTest.cs index 42a1e4385..1e6b57eab 100644 --- a/test/Dapr.Client.Test/TypeConvertersTest.cs +++ b/test/Dapr.Client.Test/TypeConvertersTest.cs @@ -14,7 +14,6 @@ namespace Dapr.Client.Test { using System.Text.Json; - using Dapr.Client.Autogen.Test.Grpc.v1; using FluentAssertions; using Xunit; diff --git a/test/Dapr.E2E.Test.App.Grpc/Startup.cs b/test/Dapr.E2E.Test.App.Grpc/Startup.cs index 531fb91b3..651f0c76b 100644 --- a/test/Dapr.E2E.Test.App.Grpc/Startup.cs +++ b/test/Dapr.E2E.Test.App.Grpc/Startup.cs @@ -1,23 +1,18 @@ -// ------------------------------------------------------------------------ -// 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.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +// ------------------------------------------------------------------------ +// 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.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/test/Dapr.E2E.Test.App/Actors/StateActor.cs b/test/Dapr.E2E.Test.App/Actors/StateActor.cs index 71a952e0f..76e9745ff 100644 --- a/test/Dapr.E2E.Test.App/Actors/StateActor.cs +++ b/test/Dapr.E2E.Test.App/Actors/StateActor.cs @@ -12,7 +12,6 @@ // ------------------------------------------------------------------------ using System; -using System.Text.Json; using System.Threading.Tasks; using Dapr.Actors.Runtime; diff --git a/test/Dapr.E2E.Test.App/Controllers/TestController.cs b/test/Dapr.E2E.Test.App/Controllers/TestController.cs index 4e475c197..1f9274d0e 100644 --- a/test/Dapr.E2E.Test.App/Controllers/TestController.cs +++ b/test/Dapr.E2E.Test.App/Controllers/TestController.cs @@ -15,8 +15,6 @@ namespace Dapr.E2E.Test { using System; using System.Threading.Tasks; - using Dapr; - using Dapr.Client; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; diff --git a/test/Dapr.E2E.Test.App/Startup.cs b/test/Dapr.E2E.Test.App/Startup.cs index 7e7484d54..05c633000 100644 --- a/test/Dapr.E2E.Test.App/Startup.cs +++ b/test/Dapr.E2E.Test.App/Startup.cs @@ -13,7 +13,6 @@ namespace Dapr.E2E.Test { - using Dapr.E2E.Test.Actors.Reentrancy; using Dapr.E2E.Test.Actors.Reminders; using Dapr.E2E.Test.Actors.Timers; using Dapr.E2E.Test.Actors.State; @@ -30,7 +29,6 @@ namespace Dapr.E2E.Test using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System.Threading.Tasks; - using System; using Microsoft.Extensions.Logging; using Serilog; diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs index 184a40448..7a1408a0e 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.StateTests.cs @@ -13,7 +13,6 @@ namespace Dapr.E2E.Test { using System; - using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Dapr.Actors; diff --git a/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs b/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs index da49e6721..78fed3d30 100644 --- a/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs +++ b/test/Dapr.E2E.Test/ServiceInvocation/E2ETests.ServiceInvocationTests.cs @@ -13,15 +13,11 @@ namespace Dapr.E2E.Test { using System; - using System.Net; using System.Net.Http; using System.Net.Http.Json; - using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Dapr.Client; - using Google.Protobuf.WellKnownTypes; - using Grpc.Core; using Xunit; public partial class E2ETests diff --git a/test/Shared/GrpcUtils.cs b/test/Shared/GrpcUtils.cs index 2dfdf8082..fd7ddf151 100644 --- a/test/Shared/GrpcUtils.cs +++ b/test/Shared/GrpcUtils.cs @@ -19,7 +19,6 @@ namespace Dapr using System.Net; using System.Net.Http; using System.Net.Http.Headers; - using System.Text.Json; using System.Threading.Tasks; using Google.Protobuf; using Grpc.Core; From 03038fa519670b583eabcef1417eacd55c3e44c8 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:20:31 +0100 Subject: [PATCH 7/7] Incremental source generator for actors (#1334) * Samples - Add k8s deployment yaml to DemoActor sample (#1308) * up Signed-off-by: Manuel Menegazzo * Fixed build Signed-off-by: Manuel Menegazzo * Added scripts for image build Signed-off-by: Manuel Menegazzo * Added readme Build and push Docker image Signed-off-by: Manuel Menegazzo * Added demo-actor.yaml Signed-off-by: Manuel Menegazzo * Fixed typo Signed-off-by: Manuel Menegazzo * Updated guide, fixed invocation throw curl Signed-off-by: Manuel Menegazzo * Removed dockerfile, updated readme, removed ps1 and sh scripts Signed-off-by: Manuel Menegazzo * Updated base image Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Signed-off-by: Manuel Menegazzo * Update demo-actor.yaml Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Signed-off-by: Manuel Menegazzo * Added overload for DaprClient DI registration (#1289) * Added overload for DaprClient DI registration allowing the consumer to easily use values from injected services (e.g. IConfiguration). Signed-off-by: Whit Waldo * Added supporting unit test Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo Co-authored-by: Phillip Hoff Signed-off-by: Manuel Menegazzo * Merge `release-1.13` back into `master` (#1285) * Update protos and related use for Dapr 1.13. (#1236) * Update protos and related use. Signed-off-by: Phillip Hoff * Update Dapr runtime version. Signed-off-by: Phillip Hoff * Init properties. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff * Update artifact action versions. (#1240) Signed-off-by: Phillip Hoff * Make recursive true as default (#1243) Signed-off-by: Shivam Kumar * Fix for secret key transformation in multi-value scenarios (#1274) * Add repro test. Signed-off-by: Phillip Hoff * Fix for secret key transformation in multi-value scenarios. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff * Update Dapr version numbers used during testing. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff Signed-off-by: Shivam Kumar Co-authored-by: Shivam Kumar Signed-off-by: Manuel Menegazzo --------- Signed-off-by: Manuel Menegazzo Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Signed-off-by: Whit Waldo Signed-off-by: Phillip Hoff Signed-off-by: Shivam Kumar Co-authored-by: Whit Waldo Co-authored-by: Phillip Hoff Co-authored-by: Shivam Kumar Signed-off-by: Manuel Menegazzo * Aligned nuget version Signed-off-by: Manuel Menegazzo * UP Signed-off-by: Manuel Menegazzo * UP Signed-off-by: Manuel Menegazzo * Debug profile added Signed-off-by: Manuel Menegazzo * Updated implementation Signed-off-by: Manuel Menegazzo * Emitted DAPR001 Diagnostic warning Signed-off-by: Manuel Menegazzo * Added DAPR002 diagnostic Signed-off-by: Manuel Menegazzo * Cleaun Signed-off-by: Manuel Menegazzo * UP Signed-off-by: Manuel Menegazzo * Added summaries Signed-off-by: Manuel Menegazzo * Added base interface to ActorClient Signed-off-by: Manuel Menegazzo * Updated Signed-off-by: Manuel Menegazzo * Added ctor Signed-off-by: Manuel Menegazzo * Added nullable directive Signed-off-by: Manuel Menegazzo * Added null check for actorproxy ctor parameter Signed-off-by: Manuel Menegazzo * Moved DiagnoticException in a dedicate cs file Signed-off-by: Manuel Menegazzo * Moved generator costants to dedicated class Signed-off-by: Manuel Menegazzo * Added ActorReference creation from the ActorBase class informations (#1277) * Handled creation of ActorReference from Actor base class Signed-off-by: Manuel Menegazzo * Updated null check Signed-off-by: Manuel Menegazzo * Added unit test for GetActorReference from null actore and actor proxy Signed-off-by: Manuel Menegazzo * Added test for ActorReference created inside Actor implementation Signed-off-by: Manuel Menegazzo * Updated description Signed-off-by: Manuel Menegazzo * Fixed test method naming Signed-off-by: Manuel Menegazzo * Added unit test for exception generated in case the type is not convertible to an ActorReference Signed-off-by: Manuel Menegazzo --------- Signed-off-by: Manuel Menegazzo * Added overload to support SDK supplying query string on invoked URL (#1310) * Refactored extensions and their tests into separate directories Signed-off-by: Whit Waldo * Added overload to method invocation to allow query string parameters to be passed in via the SDK instead of being uncermoniously added to the end of the produced HttpRequestMessage URI Signed-off-by: Whit Waldo * Added unit tests to support implementation Signed-off-by: Whit Waldo * Marking HttpExtensions as internal to prevent external usage and updating to work against Uri instead of HttpRequestMessage. Signed-off-by: Whit Waldo * Updated unit tests to match new extension purpose Signed-off-by: Whit Waldo * Resolved an ambiguous method invocation wherein it was taking the query string and passing it as the payload for a request. Removed the offending method and reworked the remaining configurations so there's no API impact. Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo Signed-off-by: Manuel Menegazzo * Fixed actorProxy argument null check Signed-off-by: Manuel Menegazzo * Moved ActorClientDesciptor into separta cs file Signed-off-by: Manuel Menegazzo * Moved textual templates to dedicated class Signed-off-by: Manuel Menegazzo * Updated comments, property names Signed-off-by: Manuel Menegazzo * Added argument null check to SyntaxFactoryHelpers Signed-off-by: Manuel Menegazzo * Added comments Signed-off-by: Manuel Menegazzo * Removed obsolete testing packages https://github.com/dotnet/roslyn-sdk/blob/main/src/Microsoft.CodeAnalysis.Testing/README.md#obsolete-packages Signed-off-by: Manuel Menegazzo * Adapted existing unit test to new source generated code Signed-off-by: Manuel Menegazzo * Up Signed-off-by: Manuel Menegazzo * Added tests for SyntaxFactoryHelpers Signed-off-by: Manuel Menegazzo * Updated generation of ArgumentNullException Signed-off-by: Manuel Menegazzo * Updated nullability Signed-off-by: Manuel Menegazzo * Fixed internal methods tests Signed-off-by: Manuel Menegazzo * Added test to IEnumerableExtensions Signed-off-by: Manuel Menegazzo * Unittested GetSyntaxKinds from Accessibility Signed-off-by: Manuel Menegazzo * UP Signed-off-by: Manuel Menegazzo * Updated assignment implementation of ctor body Signed-off-by: Manuel Menegazzo * Improved unit test Signed-off-by: Manuel Menegazzo * Added implementation of method generation Signed-off-by: Manuel Menegazzo * Fixed ArgumentNullException invocation Signed-off-by: Manuel Menegazzo * Added test for NameOfExpression Signed-off-by: Manuel Menegazzo * Fixed ActorProxy method invocation Signed-off-by: Manuel Menegazzo * Simplified proxy argument definition Signed-off-by: Manuel Menegazzo * Explicit generic arguments of the proxy call during generation Signed-off-by: Manuel Menegazzo * Handled cancellation token with default value Signed-off-by: Manuel Menegazzo * Fixed typo Signed-off-by: Manuel Menegazzo * Configured eol used in NormalizeWhitespace function Signed-off-by: Manuel Menegazzo * Normalized expected source Signed-off-by: Manuel Menegazzo * Moved to constat the ActorProxyTypeName Signed-off-by: Manuel Menegazzo * Fix typo Signed-off-by: Manuel Menegazzo * Created ActorProxyInvokeMethodAsync SyntaxFactoryHelper Signed-off-by: Manuel Menegazzo * Removed custom concat implementation Signed-off-by: Manuel Menegazzo * fix (#1329) Signed-off-by: Hannah Hunter Signed-off-by: Manuel Menegazzo * link to non-dapr endpoint howto (#1335) Signed-off-by: Hannah Hunter Signed-off-by: Manuel Menegazzo * Merge 1.14 release branch back into `master`. (#1337) Signed-off-by: Manuel Menegazzo * Fixed merge errors Signed-off-by: Manuel Menegazzo Signed-off-by: Manuel Menegazzo * Updated some summaries Signed-off-by: Manuel Menegazzo * Added some missing summaries Signed-off-by: Manuel Menegazzo * Fixed typo Signed-off-by: Manuel Menegazzo * Improved some summary text Signed-off-by: Manuel Menegazzo * Improved summaries Signed-off-by: Manuel Menegazzo * Handled review requests Signed-off-by: Manuel Menegazzo * Changed SyntaxFactoryHelpers accessor to internal Signed-off-by: Manuel Menegazzo --------- Signed-off-by: Manuel Menegazzo Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Signed-off-by: Whit Waldo Signed-off-by: Phillip Hoff Signed-off-by: Shivam Kumar Signed-off-by: Hannah Hunter Co-authored-by: Whit Waldo Co-authored-by: Phillip Hoff Co-authored-by: Shivam Kumar Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> --- Directory.Packages.props | 2 +- .../ActorClient/ActorClient.csproj | 41 +- .../ActorClient/IClientActor.cs | 2 +- .../ActorClientGenerator.cs | 454 +++++++++--------- .../AnalyzerReleases.Shipped.md | 8 + .../AnalyzerReleases.Unshipped.md | 3 + src/Dapr.Actors.Generators/Constants.cs | 38 ++ .../Dapr.Actors.Generators.csproj | 64 +-- ...CancellationTokensMustBeTheLastArgument.cs | 25 + ...tOptionallyFollowedByACancellationToken.cs | 25 + .../DiagnosticsException.cs | 25 + .../Extensions/IEnumerableExtensions.cs | 34 ++ .../Helpers/SyntaxFactoryHelpers.cs | 159 ++++++ .../Models/ActorClientDescriptor.cs | 46 ++ .../Properties/launchSettings.json | 8 + src/Dapr.Actors.Generators/Templates.cs | 87 ++++ .../ActorClientGeneratorTests.cs | 292 +++++------ .../CSharpSourceGeneratorVerifier.cs | 23 +- .../Dapr.Actors.Generators.Test.csproj | 5 +- .../Extensions/IEnumerableExtensionsTests.cs | 52 ++ .../Helpers/SyntaxFactoryHelpersTests.cs | 133 +++++ 21 files changed, 1090 insertions(+), 436 deletions(-) create mode 100644 src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md create mode 100644 src/Dapr.Actors.Generators/AnalyzerReleases.Unshipped.md create mode 100644 src/Dapr.Actors.Generators/Constants.cs create mode 100644 src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs create mode 100644 src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs create mode 100644 src/Dapr.Actors.Generators/DiagnosticsException.cs create mode 100644 src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs create mode 100644 src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs create mode 100644 src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs create mode 100644 src/Dapr.Actors.Generators/Properties/launchSettings.json create mode 100644 src/Dapr.Actors.Generators/Templates.cs create mode 100644 test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs create mode 100644 test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 3c1459b5d..332939a5b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,7 +21,7 @@ - + diff --git a/examples/GeneratedActor/ActorClient/ActorClient.csproj b/examples/GeneratedActor/ActorClient/ActorClient.csproj index 73b5c2027..88f75663d 100644 --- a/examples/GeneratedActor/ActorClient/ActorClient.csproj +++ b/examples/GeneratedActor/ActorClient/ActorClient.csproj @@ -1,22 +1,29 @@ - + - - Exe - net6 - 10.0 - enable - enable - + + Exe + net6 + 10.0 + enable + enable - - - - + + true + + + + + - - - + + + + + + + + diff --git a/examples/GeneratedActor/ActorClient/IClientActor.cs b/examples/GeneratedActor/ActorClient/IClientActor.cs index c5c732cb9..c687ecf03 100644 --- a/examples/GeneratedActor/ActorClient/IClientActor.cs +++ b/examples/GeneratedActor/ActorClient/IClientActor.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2023 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. diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index f95fc4224..001604d53 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -11,7 +11,13 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Collections.Immutable; +using Dapr.Actors.Generators.Diagnostics; +using Dapr.Actors.Generators.Extensions; +using Dapr.Actors.Generators.Helpers; +using Dapr.Actors.Generators.Models; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Dapr.Actors.Generators; @@ -20,283 +26,265 @@ namespace Dapr.Actors.Generators; /// Generates strongly-typed actor clients that use the non-remoting actor proxy. /// [Generator] -public sealed class ActorClientGenerator : ISourceGenerator +public sealed class ActorClientGenerator : IIncrementalGenerator { - private const string GeneratorsNamespace = "Dapr.Actors.Generators"; - - private const string ActorMethodAttributeTypeName = "ActorMethodAttribute"; - private const string ActorMethodAttributeFullTypeName = GeneratorsNamespace + "." + ActorMethodAttributeTypeName; - - private const string GenerateActorClientAttribute = "GenerateActorClientAttribute"; - private const string GenerateActorClientAttributeFullTypeName = GeneratorsNamespace + "." + GenerateActorClientAttribute; - - private const string ActorMethodAttributeText = $@" - // - - #nullable enable - - using System; - - namespace {GeneratorsNamespace} - {{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - internal sealed class ActorMethodAttribute : Attribute - {{ - public string? Name {{ get; set; }} - }} - }}"; - - private const string GenerateActorClientAttributeText = $@" - // - - #nullable enable - - using System; - - namespace {GeneratorsNamespace} - {{ - [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - internal sealed class GenerateActorClientAttribute : Attribute - {{ - public string? Name {{ get; set; }} - - public string? Namespace {{ get; set; }} - }} - }}"; - - private sealed class ActorInterfaceSyntaxReceiver : ISyntaxContextReceiver + /// + public void Initialize(IncrementalGeneratorInitializationContext context) { - private readonly List models = new(); - - public IEnumerable Models => this.models; - - #region ISyntaxContextReceiver Members - - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) + // Register the source output that generates the attribute definitions for ActorMethodAttribute and GenerateActorClientAttribute. + context.RegisterPostInitializationOutput(context => { - if (context.Node is not InterfaceDeclarationSyntax interfaceDeclarationSyntax - || interfaceDeclarationSyntax.AttributeLists.Count == 0) - { - return; - } - - var interfaceSymbol = context.SemanticModel.GetDeclaredSymbol(interfaceDeclarationSyntax) as INamedTypeSymbol; - - if (interfaceSymbol is null - || !interfaceSymbol.GetAttributes().Any(a => a.AttributeClass?.ToString() == GenerateActorClientAttributeFullTypeName)) - { - return; - } - - this.models.Add(interfaceSymbol); - } - - #endregion + context.AddSource( + $"{Constants.ActorMethodAttributeFullTypeName}.g.cs", + Templates.ActorMethodAttributeSourceText(Constants.GeneratorsNamespace)); + + context.AddSource( + $"{Constants.GenerateActorClientAttributeFullTypeName}.g.cs", + Templates.GenerateActorClientAttributeSourceText(Constants.GeneratorsNamespace)); + }); + + // Register the value provider that triggers the generation of actor clients when detecting the GenerateActorClientAttribute. + IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider + .ForAttributeWithMetadataName( + Constants.GenerateActorClientAttributeFullTypeName, + predicate: static (_, _) => true, + transform: static (gasc, cancellationToken) => CreateActorClientDescriptor(gasc, cancellationToken)); + + // Register the source output that generates the actor clients. + context.RegisterSourceOutput(actorClientsToGenerate, GenerateActorClientCode); } - #region ISourceGenerator Members - - /// - public void Execute(GeneratorExecutionContext context) + /// + /// Returns the descriptor for the actor client to generate. + /// + /// Current generator syntax context passed from generator pipeline. + /// Cancellation token used to interrupt the generation. + /// Returns the descriptor of actor client to generate. + private static ActorClientDescriptor CreateActorClientDescriptor( + GeneratorAttributeSyntaxContext context, + CancellationToken cancellationToken) { - if (context.SyntaxContextReceiver is not ActorInterfaceSyntaxReceiver actorInterfaceSyntaxReceiver) - { - return; - } - - var actorMethodAttributeSymbol = context.Compilation.GetTypeByMetadataName(ActorMethodAttributeFullTypeName) ?? throw new InvalidOperationException("Could not find ActorMethodAttribute."); - var generateActorClientAttributeSymbol = context.Compilation.GetTypeByMetadataName(GenerateActorClientAttributeFullTypeName) ?? throw new InvalidOperationException("Could not find GenerateActorClientAttribute."); - var cancellationTokenSymbol = context.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") ?? throw new InvalidOperationException("Could not find CancellationToken."); - - foreach (var interfaceSymbol in actorInterfaceSyntaxReceiver.Models) - { - try - { - var fullyQualifiedActorInterfaceTypeName = interfaceSymbol.ToString(); - - var attributeData = interfaceSymbol.GetAttributes().Single(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); - - var accessibility = GetClientAccessibility(interfaceSymbol); - var clientTypeName = GetClientName(interfaceSymbol, attributeData); - var namespaceName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Namespace").Value.Value?.ToString() ?? interfaceSymbol.ContainingNamespace.ToDisplayString(); - - var members = interfaceSymbol.GetMembers().OfType().Where(m => m.MethodKind == MethodKind.Ordinary).ToList(); + // Return the attribute data of GenerateActorClientAttribute, which is the attribute that triggered this generator + // and is expected to be the only attribute in the list of matching attributes. + var attributeData = context.Attributes.Single(); - var methodImplementations = String.Join("\n", members.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + var actorInterfaceSymbol = (INamedTypeSymbol)context.TargetSymbol; - var source = $@" -// + // Use the namespace specified in the GenerateActorClientAttribute, or the namespace of the actor interface if not specified. + var namespaceName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Namespace").Value.Value?.ToString() + ?? actorInterfaceSymbol.ContainingNamespace.ToDisplayString(); -namespace {namespaceName} -{{ - {accessibility} sealed class {clientTypeName} : {fullyQualifiedActorInterfaceTypeName} - {{ - private readonly Dapr.Actors.Client.ActorProxy actorProxy; + // Use the name specified in the GenerateActorClientAttribute, or the name of the actor interface with a "Client" suffix if not specified. + var clientName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Name").Value.Value?.ToString() + ?? $"{(actorInterfaceSymbol.Name.StartsWith("I") ? actorInterfaceSymbol.Name.Substring(1) : actorInterfaceSymbol.Name)}Client"; - public {clientTypeName}(Dapr.Actors.Client.ActorProxy actorProxy) - {{ - this.actorProxy = actorProxy; - }} + // Actor member to generate the client for. + var members = actorInterfaceSymbol + .GetMembers() + .OfType() + .Where(m => m.MethodKind == MethodKind.Ordinary) + .ToImmutableArray(); - {methodImplementations} - }} -}} -"; - // Add the source code to the compilation - context.AddSource($"{namespaceName}.{clientTypeName}.g.cs", source); - } - catch (DiagnosticsException e) - { - foreach (var diagnostic in e.Diagnostics) - { - context.ReportDiagnostic(diagnostic); - } - } - } + return new ActorClientDescriptor + { + NamespaceName = namespaceName, + ClientTypeName = clientName, + Methods = members, + Accessibility = actorInterfaceSymbol.DeclaredAccessibility, + InterfaceType = actorInterfaceSymbol, + Compilation = context.SemanticModel.Compilation, + }; } - /// - public void Initialize(GeneratorInitializationContext context) + /// + /// Generates the actor client code based on the specified descriptor. + /// + /// Context passed from the source generator when it has registered an output. + /// Descriptor of actor client to generate. + /// Throws when one or more required symbols assembly are missing. + private static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor descriptor) { - /* - while (!Debugger.IsAttached) + try { - System.Threading.Thread.Sleep(500); - } - */ - - context.RegisterForPostInitialization( - i => - { - i.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); - i.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); - }); + var actorMethodAttributeSymbol = descriptor.Compilation.GetTypeByMetadataName(Constants.ActorMethodAttributeFullTypeName) + ?? throw new InvalidOperationException("Could not find ActorMethodAttribute type."); - context.RegisterForSyntaxNotifications(() => new ActorInterfaceSyntaxReceiver()); - } + var cancellationTokenSymbol = descriptor.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") + ?? throw new InvalidOperationException("Could not find CancellationToken type."); - #endregion + var actorClientBaseInterface = SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(descriptor.InterfaceType.ToString())); + var autoGeneratedComment = SyntaxFactory.Comment("// "); + var nullableAnnotation = SyntaxFactory.Trivia(SyntaxFactory.NullableDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.EnableKeyword), true)); + var actorProxyTypeSyntax = SyntaxFactory.ParseTypeName(Constants.ActorProxyTypeName); - private static string GetClientAccessibility(INamedTypeSymbol interfaceSymbol) - { - return interfaceSymbol.DeclaredAccessibility switch + // Generate the actor proxy field to store the actor proxy instance. + var actorProxyFieldDeclaration = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(actorProxyTypeSyntax) + .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) + .WithModifiers(SyntaxFactory.TokenList(new[] + { + SyntaxFactory.Token(SyntaxKind.PrivateKeyword), + SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword) + })); + + // Generate the constructor for the actor client. + var actorCtor = SyntaxFactory.ConstructorDeclaration(SyntaxFactory.Identifier(descriptor.ClientTypeName)) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) + .WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(new[] + { + SyntaxFactory.Parameter(SyntaxFactory.Identifier("actorProxy")).WithType(actorProxyTypeSyntax) + }))) + .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] + { + SyntaxFactoryHelpers.ThrowIfArgumentNull("actorProxy"), + SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + SyntaxFactory.IdentifierName("actorProxy")), + SyntaxFactory.IdentifierName("actorProxy")) + ), + }))); + + var actorMethods = descriptor.Methods + .OrderBy(member => member.DeclaredAccessibility) + .ThenBy(member => member.Name) + .Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol)); + + var actorMembers = new List() + .Append(actorProxyFieldDeclaration) + .Append(actorCtor) + .Concat(actorMethods); + + var actorClientClassModifiers = new List() + .Concat(SyntaxFactoryHelpers.GetSyntaxKinds(descriptor.Accessibility)) + .Append(SyntaxKind.SealedKeyword) + .Select(sk => SyntaxFactory.Token(sk)); + + var actorClientClassDeclaration = SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName) + .WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers)) + .WithMembers(SyntaxFactory.List(actorMembers)) + .WithBaseList(SyntaxFactory.BaseList( + SyntaxFactory.Token(SyntaxKind.ColonToken), + SyntaxFactory.SeparatedList(new[] { actorClientBaseInterface }))); + + var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(descriptor.NamespaceName)) + .WithMembers(SyntaxFactory.List(new[] { actorClientClassDeclaration })) + .WithLeadingTrivia(SyntaxFactory.TriviaList(new[] { + autoGeneratedComment, + nullableAnnotation, + })); + + var compilationOutput = SyntaxFactory.CompilationUnit() + .WithMembers(SyntaxFactory.SingletonList(namespaceDeclaration)) + .NormalizeWhitespace() + .ToFullString(); + + context.AddSource($"{descriptor.FullyQualifiedTypeName}.g.cs", compilationOutput); + } + catch (DiagnosticsException e) { - Accessibility.Public => "public", - Accessibility.Internal => "internal", - Accessibility.Private => "private", - Accessibility.Protected => "protected", - Accessibility.ProtectedAndInternal => "protected internal", - _ => throw new InvalidOperationException("Unexpected accessibility.") - }; - } - - private static string GetClientName(INamedTypeSymbol interfaceSymbol, AttributeData attributeData) - { - string? clientName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Name").Value.Value?.ToString(); - - clientName ??= $"{(interfaceSymbol.Name.StartsWith("I") ? interfaceSymbol.Name.Substring(1) : interfaceSymbol.Name)}Client"; - - return clientName; + foreach (var diagnostic in e.Diagnostics) + { + context.ReportDiagnostic(diagnostic); + } + } } - private static string GenerateMethodImplementation(IMethodSymbol method, INamedTypeSymbol generateActorClientAttributeSymbol, INamedTypeSymbol cancellationTokenSymbol) + /// + /// Generates the method implementation for the specified method. + /// + /// + /// MethodSymbol extracted from the actor interface representing the method to generate. + /// + /// + /// ActorMethodAttribute symbol used to extract the original actor method name to use when making runtime calls. + /// + /// Symbol used to search the position of cancellationToken between method parameters. + /// Returns a of the generated method. + private static MethodDeclarationSyntax GenerateMethodImplementation( + IMethodSymbol method, + INamedTypeSymbol generateActorClientAttributeSymbol, + INamedTypeSymbol cancellationTokenSymbol) { int cancellationTokenIndex = method.Parameters.IndexOf(p => p.Type.Equals(cancellationTokenSymbol, SymbolEqualityComparer.Default)); var cancellationTokenParameter = cancellationTokenIndex != -1 ? method.Parameters[cancellationTokenIndex] : null; + var diagnostics = new List(); if (cancellationTokenParameter is not null && cancellationTokenIndex != method.Parameters.Length - 1) { - throw new DiagnosticsException(new[] - { - Diagnostic.Create( - new DiagnosticDescriptor( - "DAPR0001", - "Invalid method signature.", - "Cancellation tokens must be the last argument.", - "Dapr.Actors.Generators", - DiagnosticSeverity.Error, - true), - cancellationTokenParameter.Locations.First()) - }); + diagnostics.Add(CancellationTokensMustBeTheLastArgument.CreateDiagnostic(cancellationTokenParameter)); } - if ((method.Parameters.Length > 1 && cancellationTokenIndex == -1) - || (method.Parameters.Length > 2)) + if ((method.Parameters.Length > 1 && cancellationTokenIndex == -1) || (method.Parameters.Length > 2)) { - throw new DiagnosticsException(new[] - { - Diagnostic.Create( - new DiagnosticDescriptor( - "DAPR0002", - "Invalid method signature.", - "Only methods with a single argument or a single argument followed by a cancellation token are supported.", - "Dapr.Actors.Generators", - DiagnosticSeverity.Error, - true), - method.Locations.First()) - }); + diagnostics.Add(MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.CreateDiagnostic(method)); } - var attributeData = method.GetAttributes().SingleOrDefault(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); - - string? actualMethodName = attributeData?.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Name").Value.Value?.ToString() ?? method.Name; - - var requestParameter = method.Parameters.Length > 0 && cancellationTokenIndex != 0 ? method.Parameters[0] : null; - - var returnTypeArgument = (method.ReturnType as INamedTypeSymbol)?.TypeArguments.FirstOrDefault(); - - string argumentDefinitions = String.Join(", ", method.Parameters.Select(p => $"{p.Type} {p.Name}")); - - if (cancellationTokenParameter is not null - && cancellationTokenParameter.IsOptional - && cancellationTokenParameter.HasExplicitDefaultValue - && cancellationTokenParameter.ExplicitDefaultValue is null) + // If there are any diagnostics, throw an exception to report them and stop the generation. + if (diagnostics.Any()) { - argumentDefinitions = argumentDefinitions + " = default"; + throw new DiagnosticsException(diagnostics); } - string argumentList = String.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); + // Get the ActorMethodAttribute data for the method, if it exists. + var attributeData = method.GetAttributes() + .SingleOrDefault(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); - string templateArgs = - returnTypeArgument is not null - ? $"<{(requestParameter is not null ? $"{requestParameter.Type}, " : "")}{returnTypeArgument}>" - : ""; + // Generate the method name to use for the Dapr actor method invocation, using the Name property of ActorMethodAttribute if specified, + // or the original method name otherwise. + var daprMethodName = attributeData?.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Name").Value.Value?.ToString() ?? method.Name; - return - $@"public {method.ReturnType} {method.Name}({argumentDefinitions}) - {{ - return this.actorProxy.InvokeMethodAsync{templateArgs}({argumentList}); - }}"; - } -} + var methodModifiers = new List() + .Concat(SyntaxFactoryHelpers.GetSyntaxKinds(method.DeclaredAccessibility)) + .Select(sk => SyntaxFactory.Token(sk)); -internal static class Extensions -{ - public static int IndexOf(this IEnumerable source, Func predicate) - { - int index = 0; + // Define the parameters to pass to the actor proxy method invocation. + // Exclude the CancellationToken parameter if it exists, because it need to be handled separately. + var methodParameters = method.Parameters + .Where(p => p.Type is not INamedTypeSymbol { Name: "CancellationToken" }) + .Select(p => SyntaxFactory.Parameter(SyntaxFactory.Identifier(p.Name)).WithType(SyntaxFactory.ParseTypeName(p.Type.ToString()))); - foreach (var item in source) + // Append the CancellationToken parameter if it exists, handling the case where it is optional and has no default value. + if (cancellationTokenParameter is not null) { - if (predicate(item)) + if (cancellationTokenParameter.IsOptional + && cancellationTokenParameter.HasExplicitDefaultValue + && cancellationTokenParameter.ExplicitDefaultValue is null) { - return index; + methodParameters = methodParameters.Append( + SyntaxFactory.Parameter(SyntaxFactory.Identifier(cancellationTokenParameter.Name)) + .WithDefault(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression))) + .WithType(SyntaxFactory.ParseTypeName(cancellationTokenParameter.Type.ToString()))); + } + else + { + methodParameters = methodParameters.Append( + SyntaxFactory.Parameter(SyntaxFactory.Identifier(cancellationTokenParameter.Name)) + .WithType(SyntaxFactory.ParseTypeName(cancellationTokenParameter.Type.ToString()))); } - - index++; } - return -1; - } -} + // Extract the return type of the original method. + var methodReturnType = (INamedTypeSymbol)method.ReturnType; -internal sealed class DiagnosticsException : Exception -{ - public DiagnosticsException(IEnumerable diagnostics) - : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) - { - this.Diagnostics = diagnostics.ToArray(); + // Generate the method implementation. + var generatedMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(method.ReturnType.ToString()), method.Name) + .WithModifiers(SyntaxFactory.TokenList(methodModifiers)) + .WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(methodParameters))) + .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] + { + SyntaxFactory.ReturnStatement(SyntaxFactoryHelpers.ActorProxyInvokeMethodAsync( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + SyntaxFactory.IdentifierName("actorProxy")), + daprMethodName, + method.Parameters, + methodReturnType.TypeArguments + )), + }))); + + return generatedMethod; } - - public IEnumerable Diagnostics { get; } } diff --git a/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md b/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md new file mode 100644 index 000000000..62b61ac2c --- /dev/null +++ b/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md @@ -0,0 +1,8 @@ +## Release 1.14 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|-------------------- +DAPR0001| Usage | Error | Cancellation tokens must be the last argument +DAPR0002| Usage | Error | Only methods with a single argument or a single argument followed by a cancellation token are supported \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/AnalyzerReleases.Unshipped.md b/src/Dapr.Actors.Generators/AnalyzerReleases.Unshipped.md new file mode 100644 index 000000000..b1b99aaf2 --- /dev/null +++ b/src/Dapr.Actors.Generators/AnalyzerReleases.Unshipped.md @@ -0,0 +1,3 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/Dapr.Actors.Generators/Constants.cs b/src/Dapr.Actors.Generators/Constants.cs new file mode 100644 index 000000000..392def4ef --- /dev/null +++ b/src/Dapr.Actors.Generators/Constants.cs @@ -0,0 +1,38 @@ +namespace Dapr.Actors.Generators +{ + /// + /// Constants used by the code generator. + /// + internal static class Constants + { + /// + /// The namespace used by the generated code. + /// + public const string GeneratorsNamespace = "Dapr.Actors.Generators"; + + /// + /// 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 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; + + /// + /// Actor proxy type name. + /// + public const string ActorProxyTypeName = "Dapr.Actors.Client.ActorProxy"; + } +} diff --git a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj index 370d422f1..b1f73383a 100644 --- a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj +++ b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj @@ -1,45 +1,55 @@ - + - - enable - enable - + + enable + enable + true + - - true - + + true + - - - - netstandard2.0 - + + + netstandard2.0 + - - false + + false - - true + + true - - false + + false - - This package contains source generators for interacting with Actor services using Dapr. - $(PackageTags);Actors - + + This package contains source generators for interacting with Actor services using Dapr. + $(PackageTags);Actors + - - - - + + + + + + + + + + + + + diff --git a/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs new file mode 100644 index 000000000..376bb360f --- /dev/null +++ b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs @@ -0,0 +1,25 @@ +using Microsoft.CodeAnalysis; + +namespace Dapr.Actors.Generators.Diagnostics +{ + 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"; + + 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)); + } +} diff --git a/src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs b/src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs new file mode 100644 index 000000000..c82b20630 --- /dev/null +++ b/src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs @@ -0,0 +1,25 @@ +using Microsoft.CodeAnalysis; + +namespace Dapr.Actors.Generators.Diagnostics +{ + 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"; + + 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)); + } +} diff --git a/src/Dapr.Actors.Generators/DiagnosticsException.cs b/src/Dapr.Actors.Generators/DiagnosticsException.cs new file mode 100644 index 000000000..d196f8484 --- /dev/null +++ b/src/Dapr.Actors.Generators/DiagnosticsException.cs @@ -0,0 +1,25 @@ +using Microsoft.CodeAnalysis; + +namespace Dapr.Actors.Generators +{ + /// + /// Exception thrown when diagnostics are encountered during code generation. + /// + internal sealed class DiagnosticsException : Exception + { + /// + /// 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; } + } +} diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs new file mode 100644 index 000000000..6b45e86f3 --- /dev/null +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,34 @@ +namespace Dapr.Actors.Generators.Extensions +{ + 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) + { + if (predicate is null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + int index = 0; + + foreach (var item in source) + { + if (predicate(item)) + { + return index; + } + + index++; + } + + return -1; + } + } +} diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs new file mode 100644 index 000000000..36df7b280 --- /dev/null +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -0,0 +1,159 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Dapr.Actors.Generators.Helpers +{ + /// + /// Syntax factory helpers for generating syntax. + /// + internal static partial class SyntaxFactoryHelpers + { + /// + /// 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[] + { + SyntaxFactory.ExpressionStatement(ThrowArgumentNullException(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()); + + return SyntaxFactory.InvocationExpression( + SyntaxFactory.IdentifierName(nameofIdentifier), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] + { + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName)) + })) + ); + } + + /// + /// 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()))); + + // 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)))); + + // 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; + } + + /// + /// 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(); + + 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; + } + } +} diff --git a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs new file mode 100644 index 000000000..e1f54fac4 --- /dev/null +++ b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs @@ -0,0 +1,46 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; + +namespace Dapr.Actors.Generators.Models +{ + /// + /// Describes an actor client to generate. + /// + 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!; + } +} diff --git a/src/Dapr.Actors.Generators/Properties/launchSettings.json b/src/Dapr.Actors.Generators/Properties/launchSettings.json new file mode 100644 index 000000000..f146e6195 --- /dev/null +++ b/src/Dapr.Actors.Generators/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Debug": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\examples\\GeneratedActor\\ActorClient\\ActorClient.csproj" + } + } +} \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/Templates.cs b/src/Dapr.Actors.Generators/Templates.cs new file mode 100644 index 000000000..6cc4c9f87 --- /dev/null +++ b/src/Dapr.Actors.Generators/Templates.cs @@ -0,0 +1,87 @@ +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; + +namespace Dapr.Actors.Generators +{ + /// + /// Templates for generating source code. + /// + internal static partial class Templates + { + /// + /// 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) + { + throw new ArgumentNullException(nameof(destinationNamespace)); + } + + var source = $@" +// + +#nullable enable + +using System; + +namespace {destinationNamespace} +{{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + internal sealed class {Constants.ActorMethodAttributeTypeName} : Attribute + {{ + public string? Name {{ get; set; }} + }} +}}"; + + 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) + { + if (destinationNamespace is null) + { + throw new ArgumentNullException(nameof(destinationNamespace)); + } + + string source = $@" +// + +#nullable enable + +using System; + +namespace {destinationNamespace} +{{ + [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class {Constants.GenerateActorClientAttributeTypeName} : Attribute + {{ + public string? Name {{ get; set; }} + + public string? Namespace {{ get; set; }} + }} +}}"; + + return SourceText.From( + SyntaxFactory.ParseCompilationUnit(source) + .NormalizeWhitespace() + .ToFullString(), + Encoding.UTF8); + } + } +} diff --git a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs index ce4c0accd..4c0ef194e 100644 --- a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs +++ b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2023 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. @@ -21,43 +21,40 @@ namespace Dapr.Actors.Generators; public sealed class ActorClientGeneratorTests { - private const string ActorMethodAttributeText = $@" - // - - #nullable enable - - using System; - - namespace Dapr.Actors.Generators - {{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - internal sealed class ActorMethodAttribute : Attribute - {{ - public string? Name {{ get; set; }} - }} - }}"; - - private static readonly (string, SourceText) ActorMethodAttributeSource = ("Dapr.Actors.Generators/Dapr.Actors.Generators.ActorClientGenerator/Dapr.Actors.Generators.ActorMethodAttribute.g.cs", SourceText.From(ActorMethodAttributeText, Encoding.UTF8)); - - private const string GenerateActorClientAttributeText = $@" - // - - #nullable enable - - using System; - - namespace Dapr.Actors.Generators - {{ - [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - internal sealed class GenerateActorClientAttribute : Attribute - {{ - public string? Name {{ get; set; }} - - public string? Namespace {{ get; set; }} - }} - }}"; - - private static readonly (string, SourceText) GenerateActorClientAttributeSource = ("Dapr.Actors.Generators/Dapr.Actors.Generators.ActorClientGenerator/Dapr.Actors.Generators.GenerateActorClientAttribute.g.cs", SourceText.From(GenerateActorClientAttributeText, Encoding.UTF8)); + private const string ActorMethodAttributeText = $@"// +#nullable enable +using System; + +namespace Dapr.Actors.Generators +{{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + internal sealed class ActorMethodAttribute : Attribute + {{ + public string? Name {{ get; set; }} + }} +}}"; + + private static readonly (string, SourceText) ActorMethodAttributeSource = ( + Path.Combine("Dapr.Actors.Generators", "Dapr.Actors.Generators.ActorClientGenerator", "Dapr.Actors.Generators.ActorMethodAttribute.g.cs"), + SourceText.From(ActorMethodAttributeText, Encoding.UTF8)); + + private const string GenerateActorClientAttributeText = $@"// +#nullable enable +using System; + +namespace Dapr.Actors.Generators +{{ + [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class GenerateActorClientAttribute : Attribute + {{ + public string? Name {{ get; set; }} + public string? Namespace {{ get; set; }} + }} +}}"; + + private static readonly (string, SourceText) GenerateActorClientAttributeSource = ( + Path.Combine("Dapr.Actors.Generators", "Dapr.Actors.Generators.ActorClientGenerator", "Dapr.Actors.Generators.GenerateActorClientAttribute.g.cs"), + SourceText.From(GenerateActorClientAttributeText, Encoding.UTF8)); private static VerifyCS.Test CreateTest(string originalSource, string? generatedName = null, string? generatedSource = null) { @@ -77,7 +74,9 @@ private static VerifyCS.Test CreateTest(string originalSource, string? generated if (generatedName is not null && generatedSource is not null) { - test.TestState.GeneratedSources.Add(($"Dapr.Actors.Generators/Dapr.Actors.Generators.ActorClientGenerator/{generatedName}", SourceText.From(generatedSource, Encoding.UTF8))); + test.TestState.GeneratedSources.Add(( + Path.Combine("Dapr.Actors.Generators", "Dapr.Actors.Generators.ActorClientGenerator", generatedName), + SourceText.From(generatedSource, Encoding.UTF8))); } return test; @@ -97,20 +96,22 @@ public interface ITestActor { Task TestMethod(); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -119,8 +120,7 @@ public System.Threading.Tasks.Task TestMethod() return this.actorProxy.InvokeMethodAsync(""TestMethod""); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -139,20 +139,22 @@ internal interface ITestActor { Task TestMethod(); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace Test { internal sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -161,8 +163,7 @@ public System.Threading.Tasks.Task TestMethod() return this.actorProxy.InvokeMethodAsync(""TestMethod""); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -181,20 +182,22 @@ internal interface ITestActor { Task TestMethod(); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace Test { internal sealed class MyTestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public MyTestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -203,8 +206,7 @@ public System.Threading.Tasks.Task TestMethod() return this.actorProxy.InvokeMethodAsync(""TestMethod""); } } -} -"; +}"; await CreateTest(originalSource, "Test.MyTestActorClient.g.cs", generatedSource).RunAsync(); } @@ -223,20 +225,22 @@ internal interface ITestActor { Task TestMethod(); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace MyTest { internal sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -245,8 +249,7 @@ public System.Threading.Tasks.Task TestMethod() return this.actorProxy.InvokeMethodAsync(""TestMethod""); } } -} -"; +}"; await CreateTest(originalSource, "MyTest.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -269,17 +272,20 @@ public interface ITestActor } "; - var generatedSource = @" -// - + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -288,8 +294,7 @@ public System.Threading.Tasks.Task TestMethod() return this.actorProxy.InvokeMethodAsync(""MyTestMethod""); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -313,27 +318,29 @@ public interface ITestActor } "; - var generatedSource = @" -// - + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } public System.Threading.Tasks.Task TestMethod(Test.TestValue value) { - return this.actorProxy.InvokeMethodAsync(""TestMethod"", value); + return this.actorProxy.InvokeMethodAsync(""TestMethod"", value); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -354,20 +361,22 @@ public interface ITestActor { Task TestMethodAsync(); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -376,8 +385,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) return this.actorProxy.InvokeMethodAsync(""TestMethodAsync""); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -400,20 +408,22 @@ public interface ITestActor { Task TestMethodAsync(TestRequestValue value); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -422,8 +432,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", value); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -443,20 +452,22 @@ public interface ITestActor { Task TestMethodAsync(CancellationToken cancellationToken); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -465,8 +476,7 @@ public System.Threading.Tasks.Task TestMethodAsync(System.Threading.Cancellation return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", cancellationToken); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -486,20 +496,22 @@ public interface ITestActor { Task TestMethodAsync(CancellationToken cancellationToken = default); } -} -"; - - var generatedSource = @" -// +}"; + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } @@ -508,8 +520,7 @@ public System.Threading.Tasks.Task TestMethodAsync(System.Threading.Cancellation return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", cancellationToken); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -534,27 +545,29 @@ public interface ITestActor } "; - var generatedSource = @" -// - + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } public System.Threading.Tasks.Task TestMethodAsync(Test.TestValue value, System.Threading.CancellationToken cancellationToken) { - return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", value, cancellationToken); + return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", value, cancellationToken); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -579,27 +592,29 @@ public interface ITestActor } "; - var generatedSource = @" -// - + var generatedSource = @"// +#nullable enable namespace Test { public sealed class TestActorClient : Test.ITestActor { private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { + if (actorProxy is null) + { + throw new System.ArgumentNullException(nameof(actorProxy)); + } + this.actorProxy = actorProxy; } public System.Threading.Tasks.Task TestMethodAsync(Test.TestValue value, System.Threading.CancellationToken cancellationToken = default) { - return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", value, cancellationToken); + return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", value, cancellationToken); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -621,15 +636,14 @@ public interface ITestActor { Task TestMethodAsync(CancellationToken cancellationToken, int value); } -} -"; +}"; var test = CreateTest(originalSource); test.TestState.ExpectedDiagnostics.Add( new DiagnosticResult("DAPR0001", DiagnosticSeverity.Error) .WithSpan(13, 48, 13, 65) - .WithMessage("Cancellation tokens must be the last argument.")); + .WithMessage("Cancellation tokens must be the last argument")); await test.RunAsync(); } @@ -651,15 +665,14 @@ public interface ITestActor { Task TestMethodAsync(int value1, int value2); } -} -"; +}"; var test = CreateTest(originalSource); test.TestState.ExpectedDiagnostics.Add( new DiagnosticResult("DAPR0002", DiagnosticSeverity.Error) .WithSpan(13, 14, 13, 29) - .WithMessage("Only methods with a single argument or a single argument followed by a cancellation token are supported.")); + .WithMessage("Only methods with a single argument or a single argument followed by a cancellation token are supported")); await test.RunAsync(); } @@ -681,16 +694,15 @@ public interface ITestActor { Task TestMethodAsync(int value1, int value2, CancellationToken cancellationToken); } -} -"; +}"; var test = CreateTest(originalSource); test.TestState.ExpectedDiagnostics.Add( new DiagnosticResult("DAPR0002", DiagnosticSeverity.Error) .WithSpan(13, 14, 13, 29) - .WithMessage("Only methods with a single argument or a single argument followed by a cancellation token are supported.")); + .WithMessage("Only methods with a single argument or a single argument followed by a cancellation token are supported")); await test.RunAsync(); } -} \ No newline at end of file +} diff --git a/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs b/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs index 2b1046e1a..c64fd3427 100644 --- a/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs +++ b/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2023 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. @@ -16,28 +16,25 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; /// /// From Roslyn Source Generators Cookbook: https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md#unit-testing-of-generators /// internal static class CSharpSourceGeneratorVerifier - where TSourceGenerator : ISourceGenerator, new() + where TSourceGenerator : IIncrementalGenerator, new() { -#pragma warning disable CS0618 // Type or member is obsolete - public class Test : CSharpSourceGeneratorTest -#pragma warning restore CS0618 // Type or member is obsolete + public class Test : CSharpSourceGeneratorTest { public Test() { int frameworkVersion = - #if NET6_0 +#if NET6_0 6; - #elif NET7_0 +#elif NET7_0 7; - #elif NET8_0 +#elif NET8_0 8; - #endif +#endif // // NOTE: Ordinarily we'd use the following: @@ -58,10 +55,10 @@ public Test() protected override CompilationOptions CreateCompilationOptions() { - var compilationOptions = base.CreateCompilationOptions(); + var compilationOptions = base.CreateCompilationOptions(); - return compilationOptions - .WithSpecificDiagnosticOptions(compilationOptions.SpecificDiagnosticOptions.SetItems(GetNullableWarningsFromCompiler())); + return compilationOptions + .WithSpecificDiagnosticOptions(compilationOptions.SpecificDiagnosticOptions.SetItems(GetNullableWarningsFromCompiler())); } public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.Default; diff --git a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj index 80a79cafe..9e9a9e4db 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -13,9 +13,7 @@ - - - + @@ -27,7 +25,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - diff --git a/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs new file mode 100644 index 000000000..97dbcfe1e --- /dev/null +++ b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs @@ -0,0 +1,52 @@ +using Dapr.Actors.Generators.Extensions; + +namespace Dapr.Actors.Generators.Test.Extensions +{ + public class IEnumerableExtensionsTests + { + [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); + } + } +} diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs new file mode 100644 index 000000000..807bd7469 --- /dev/null +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -0,0 +1,133 @@ +using Dapr.Actors.Generators.Helpers; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Dapr.Actors.Generators.Test.Helpers +{ + public class SyntaxFactoryHelpersTests + { + [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); + } + + [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); + } + + [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(); ; + + // 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); + } + + [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 + + // Act + var generatedSyntaxKinds = SyntaxFactoryHelpers.GetSyntaxKinds(accessibility); + + // Assert + foreach (var expectedSyntaxKind in expectedSyntaxKinds) + { + Assert.Contains(expectedSyntaxKind, generatedSyntaxKinds); + } + + Assert.Equal(expectedSyntaxKinds.Count, generatedSyntaxKinds.Count); + } + } +}