From 78ef50331a804fad5d20893eda5f28d8dd87b53b Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Wed, 26 Jun 2024 18:19:53 +0200 Subject: [PATCH 01/63] 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 --- all.sln | 14 +---- examples/Actor/DemoActor/DemoActor.csproj | 29 ++++++--- examples/Actor/DemoActor/Startup.cs | 2 +- examples/Actor/DemoActor/demo-actor.yaml | 67 ++++++++++++++++++++ examples/Actor/README.md | 77 +++++++++++++++++++++++ 5 files changed, 166 insertions(+), 23 deletions(-) create mode 100644 examples/Actor/DemoActor/demo-actor.yaml diff --git a/all.sln b/all.sln index 0b95478f3..228047852 100644 --- a/all.sln +++ b/all.sln @@ -32,6 +32,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BD1276E-D28A-45EA-89B1-6AD48471500D}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.Test", "test\Dapr.Actors.AspNetCore.Test\Dapr.Actors.AspNetCore.Test.csproj", "{9C1D6ABA-5EDE-4FA0-A8A9-0AB98CB74737}" @@ -347,16 +348,3 @@ Global SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} EndGlobalSection EndGlobal -8-446B-AECD-DCC2CC871F73} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} - EndGlobalSection -EndGlobal -C991940} = {DD020B34-460F-455F-8D17-CF4A949F100B} - {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} - EndGlobalSection -EndGlobal diff --git a/examples/Actor/DemoActor/DemoActor.csproj b/examples/Actor/DemoActor/DemoActor.csproj index 1ee37fdbe..24a42ee0e 100644 --- a/examples/Actor/DemoActor/DemoActor.csproj +++ b/examples/Actor/DemoActor/DemoActor.csproj @@ -1,13 +1,24 @@  - - net6 - - - - - - - + + net6 + + + + true + true + demo-actor + + + + + + + + + + + + diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs index c04dfdcba..da2b9e764 100644 --- a/examples/Actor/DemoActor/Startup.cs +++ b/examples/Actor/DemoActor/Startup.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. diff --git a/examples/Actor/DemoActor/demo-actor.yaml b/examples/Actor/DemoActor/demo-actor.yaml new file mode 100644 index 000000000..99a8abd34 --- /dev/null +++ b/examples/Actor/DemoActor/demo-actor.yaml @@ -0,0 +1,67 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.in-memory + version: v1 + metadata: + - name: actorStateStore + value: "true" +--- +kind: Service +apiVersion: v1 +metadata: + name: demoactor + labels: + app: demoactor +spec: + selector: + app: demoactor + ports: + - name: app-port + protocol: TCP + port: 5010 + targetPort: app-port + - name: dapr-http + protocol: TCP + port: 3500 + targetPort: 3500 + type: LoadBalancer +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: demoactor + labels: + app: demoactor +spec: + replicas: 1 + selector: + matchLabels: + app: demoactor + template: + metadata: + labels: + app: demoactor + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "demoactor" + dapr.io/app-port: "5010" + dapr.io/enable-api-logging: "true" + dapr.io/sidecar-listen-addresses: "0.0.0.0" + spec: + containers: + - name: demoactor + # image: /demo-actor:latest + image: demo-actor:latest + # if you are using docker desktop, you can use imagePullPolicy: Never to use local image + imagePullPolicy: Never + env: + - name: APP_PORT + value: "5010" + - name: ASPNETCORE_URLS + value: "http://+:5010" + ports: + - name: app-port + containerPort: 5010 diff --git a/examples/Actor/README.md b/examples/Actor/README.md index ddc42cecf..a7bb46c03 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -80,3 +80,80 @@ On Windows: ```sh curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/GetData ``` + +### Build and push Docker image +You can build the docker image of `DemoActor` service by running the following commands in the `DemoActor` project directory: + +``` Bash +dotnet publish --os linux --arch x64 /t:PublishContainer -p ContainerImageTags='"latest"' --self-contained +``` + +The build produce and image with tag `demo-actor:latest` and load it in the local registry. +Now the image can be pushed to your remote Docker registry by running the following commands: + +``` Bash +# Replace with your Docker registry +docker tag demo-actor:latest /demo-actor:latest + +# Push the image to your Docker registry +docker push /demo-actor:latest +``` + +### Deploy the Actor service to Kubernetes +#### Prerequisites +- A Kubernetes cluster with `kubectl` configured to access it. +- Dapr v1.13+ installed on the Kubernetes cluster. Follow the instructions [here](https://docs.dapr.io/getting-started/install-dapr-kubernetes/). +- A Docker registry where you pushed the `DemoActor` image. + +#### Deploy the Actor service +For quick deployment you can install dapr in dev mode using the following command: + +``` Bash +dapr init -k --dev +``` + +To deploy the `DemoActor` service to Kubernetes, you can use the provided Kubernetes manifest file `demo-actor.yaml` in the `DemoActor` project directory. +Before applying the manifest file, replace the image name in the manifest file with the image name you pushed to your Docker registry. + +Part to update in `demo-actor.yaml`: +``` YAML +image: /demoactor:latest +``` + +To install the application in `default` namespace, run the following command: + +``` Bash +kubectl apply -f demo-actor.yaml +``` + +This will deploy the `DemoActor` service to Kubernetes. You can check the status of the deployment by running: + +``` Bash +kubectl get pods -n default --watch +``` + +The manifest create 2 services: + +- `demoactor` service: The service that hosts the `DemoActor` actor. +- `demoactor-dapr` service: The service that hosts the Dapr sidecar for the `DemoActor` actor. + +### Make client calls to the deployed Actor service +To make client calls to the deployed `DemoActor` service, you can use the `ActorClient` project. +Before running the client, update the `DAPR_HTTP_PORT` environment variable in the `ActorClient` project directory to the port on which Dapr is running in the Kubernetes cluster. + +On Linux, MacOS: +``` Bash +export DAPR_HTTP_PORT=3500 +``` + +Than port-forward the `DemoActor` service to your local machine: + +``` Bash +kubectl port-forward svc/demoactor 3500:3500 +``` + +Now you can run the client project from the `ActorClient` directory: + +``` Bash +dotnet run +``` \ No newline at end of file From 7711fd64a881b0bfe4b4fa30ef6c2b441d02c706 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 26 Jun 2024 22:21:05 +0200 Subject: [PATCH 02/63] Aligned nuget version Signed-off-by: Manuel Menegazzo --- .../ActorClient/ActorClient.csproj | 41 +++++++++++-------- .../Dapr.Actors.Generators.csproj | 2 +- .../CSharpSourceGeneratorVerifier.cs | 18 ++++---- .../Dapr.Actors.Generators.Test.csproj | 2 - 4 files changed, 34 insertions(+), 29 deletions(-) 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/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj index a69f2d1a0..e7f6abbe7 100644 --- a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj +++ b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj @@ -10,7 +10,7 @@ - + diff --git a/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs b/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs index 435488c2c..27d8bc5e0 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. @@ -29,13 +29,13 @@ 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: @@ -56,10 +56,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; @@ -78,4 +78,4 @@ protected override ParseOptions CreateParseOptions() return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); } } -} \ No newline at end of file +} 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 212faed2d..93f9c6d7c 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -13,8 +13,6 @@ - - From 30189bed275541e23a8fbb8f8d07cfad06f34425 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 2 Jul 2024 06:14:22 +0200 Subject: [PATCH 03/63] UP Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 64 ++-- .../ActorClientGeneratorInc.cs | 315 ++++++++++++++++++ 2 files changed, 347 insertions(+), 32 deletions(-) create mode 100644 src/Dapr.Actors.Generators/ActorClientGeneratorInc.cs diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 349d80188..d7e779e36 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -19,7 +19,7 @@ namespace Dapr.Actors.Generators; /// /// Generates strongly-typed actor clients that use the non-remoting actor proxy. /// -[Generator] +//[Generator] public sealed class ActorClientGenerator : ISourceGenerator { private const string GeneratorsNamespace = "Dapr.Actors.Generators"; @@ -194,7 +194,7 @@ private static string GetClientAccessibility(INamedTypeSymbol interfaceSymbol) 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; @@ -271,33 +271,33 @@ returnTypeArgument is not null } } -internal static class Extensions -{ - public static int IndexOf(this IEnumerable source, Func predicate) - { - int index = 0; - - foreach (var item in source) - { - if (predicate(item)) - { - return index; - } - - index++; - } - - return -1; - } -} - -internal sealed class DiagnosticsException : Exception -{ - public DiagnosticsException(IEnumerable diagnostics) - : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) - { - this.Diagnostics = diagnostics.ToArray(); - } - - public IEnumerable Diagnostics { get; } -} +//internal static class Extensions +//{ +// public static int IndexOf(this IEnumerable source, Func predicate) +// { +// int index = 0; + +// foreach (var item in source) +// { +// if (predicate(item)) +// { +// return index; +// } + +// index++; +// } + +// return -1; +// } +//} + +//internal sealed class DiagnosticsException : Exception +//{ +// public DiagnosticsException(IEnumerable diagnostics) +// : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) +// { +// this.Diagnostics = diagnostics.ToArray(); +// } + +// public IEnumerable Diagnostics { get; } +//} diff --git a/src/Dapr.Actors.Generators/ActorClientGeneratorInc.cs b/src/Dapr.Actors.Generators/ActorClientGeneratorInc.cs new file mode 100644 index 000000000..54289b305 --- /dev/null +++ b/src/Dapr.Actors.Generators/ActorClientGeneratorInc.cs @@ -0,0 +1,315 @@ +// ------------------------------------------------------------------------ +// 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. +// 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.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Dapr.Actors.Generators; + +/// +/// Generates strongly-typed actor clients that use the non-remoting actor proxy. +/// +[Generator] +public sealed class ActorClientGeneratorInc : 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 + { + private readonly List models = new(); + + public IEnumerable Models => this.models; + + #region ISyntaxContextReceiver Members + + public void OnVisitSyntaxNode(GeneratorSyntaxContext 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 + } + + #region ISourceGenerator Members + + /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + context.RegisterPostInitializationOutput(context => + { + context.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); + context.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); + }); + } + + /// + [Obsolete] + public void Execute(GeneratorExecutionContext context) + { + 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 actorInterfaceTypeName = interfaceSymbol.Name; + 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(); + + var methodImplementations = String.Join("\n", members.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + + var source = $@" +// + +namespace {namespaceName} +{{ + {accessibility} sealed class {clientTypeName} : {fullyQualifiedActorInterfaceTypeName} + {{ + private readonly Dapr.Actors.Client.ActorProxy actorProxy; + + public {clientTypeName}(Dapr.Actors.Client.ActorProxy actorProxy) + {{ + this.actorProxy = actorProxy; + }} + + {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); + } + } + } + } + + /// + [Obsolete] + public void Initialize(GeneratorInitializationContext context) + { + /* + while (!Debugger.IsAttached) + { + System.Threading.Thread.Sleep(500); + } + */ + + context.RegisterForPostInitialization( + i => + { + i.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); + i.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); + }); + + context.RegisterForSyntaxNotifications(() => new ActorInterfaceSyntaxReceiver()); + } + + #endregion + + private static string GetClientAccessibility(INamedTypeSymbol interfaceSymbol) + { + return interfaceSymbol.DeclaredAccessibility switch + { + 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; + } + + private static string 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; + + 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()) + }); + } + + 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()) + }); + } + + 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) + { + argumentDefinitions = argumentDefinitions + " = default"; + } + + string argumentList = String.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); + + string templateArgs = + returnTypeArgument is not null + ? $"<{(requestParameter is not null ? $"{requestParameter.Type}, " : "")}{returnTypeArgument}>" + : ""; + + return + $@"public {method.ReturnType} {method.Name}({argumentDefinitions}) + {{ + return this.actorProxy.InvokeMethodAsync{templateArgs}({argumentList}); + }}"; + } +} + +internal static class Extensions +{ + public static int IndexOf(this IEnumerable source, Func predicate) + { + int index = 0; + + foreach (var item in source) + { + if (predicate(item)) + { + return index; + } + + index++; + } + + return -1; + } +} + +internal sealed class DiagnosticsException : Exception +{ + public DiagnosticsException(IEnumerable diagnostics) + : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) + { + this.Diagnostics = diagnostics.ToArray(); + } + + public IEnumerable Diagnostics { get; } +} From 86c881a91558cc4565b159ac4d81fcc095849de4 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 4 Jul 2024 14:56:19 +0200 Subject: [PATCH 04/63] UP Signed-off-by: Manuel Menegazzo --- all.sln | 3 +- .../ActorClientGenerator.cs | 76 +++++++++++-------- ...ratorInc.cs => ActorClientGeneratorOld.cs} | 76 ++++++++----------- .../ActorClientGeneratorTests.cs | 2 +- 4 files changed, 79 insertions(+), 78 deletions(-) rename src/Dapr.Actors.Generators/{ActorClientGeneratorInc.cs => ActorClientGeneratorOld.cs} (90%) diff --git a/all.sln b/all.sln index 228047852..4a8fd0aca 100644 --- a/all.sln +++ b/all.sln @@ -32,7 +32,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BD1276E-D28A-45EA-89B1-6AD48471500D}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.Test", "test\Dapr.Actors.AspNetCore.Test\Dapr.Actors.AspNetCore.Test.csproj", "{9C1D6ABA-5EDE-4FA0-A8A9-0AB98CB74737}" @@ -348,3 +347,5 @@ Global SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} EndGlobalSection EndGlobal +lobalSection +EndGlobal diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index d7e779e36..04c2e2876 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -19,8 +19,8 @@ namespace Dapr.Actors.Generators; /// /// Generates strongly-typed actor clients that use the non-remoting actor proxy. /// -//[Generator] -public sealed class ActorClientGenerator : ISourceGenerator +[Generator] +public sealed class ActorClientGenerator : IIncrementalGenerator { private const string GeneratorsNamespace = "Dapr.Actors.Generators"; @@ -97,6 +97,17 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context) #region ISourceGenerator Members /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + context.RegisterPostInitializationOutput(context => + { + context.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); + context.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); + }); + } + + /// + [Obsolete] public void Execute(GeneratorExecutionContext context) { if (context.SyntaxContextReceiver is not ActorInterfaceSyntaxReceiver actorInterfaceSyntaxReceiver) @@ -157,6 +168,7 @@ namespace {namespaceName} } /// + [Obsolete] public void Initialize(GeneratorInitializationContext context) { /* @@ -271,33 +283,33 @@ returnTypeArgument is not null } } -//internal static class Extensions -//{ -// public static int IndexOf(this IEnumerable source, Func predicate) -// { -// int index = 0; - -// foreach (var item in source) -// { -// if (predicate(item)) -// { -// return index; -// } - -// index++; -// } - -// return -1; -// } -//} - -//internal sealed class DiagnosticsException : Exception -//{ -// public DiagnosticsException(IEnumerable diagnostics) -// : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) -// { -// this.Diagnostics = diagnostics.ToArray(); -// } - -// public IEnumerable Diagnostics { get; } -//} +internal static class Extensions +{ + public static int IndexOf(this IEnumerable source, Func predicate) + { + int index = 0; + + foreach (var item in source) + { + if (predicate(item)) + { + return index; + } + + index++; + } + + return -1; + } +} + +internal sealed class DiagnosticsException : Exception +{ + public DiagnosticsException(IEnumerable diagnostics) + : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) + { + this.Diagnostics = diagnostics.ToArray(); + } + + public IEnumerable Diagnostics { get; } +} diff --git a/src/Dapr.Actors.Generators/ActorClientGeneratorInc.cs b/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs similarity index 90% rename from src/Dapr.Actors.Generators/ActorClientGeneratorInc.cs rename to src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs index 54289b305..105649664 100644 --- a/src/Dapr.Actors.Generators/ActorClientGeneratorInc.cs +++ b/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs @@ -19,8 +19,8 @@ namespace Dapr.Actors.Generators; /// /// Generates strongly-typed actor clients that use the non-remoting actor proxy. /// -[Generator] -public sealed class ActorClientGeneratorInc : IIncrementalGenerator +//[Generator] +public sealed class ActorClientGeneratorOld : ISourceGenerator { private const string GeneratorsNamespace = "Dapr.Actors.Generators"; @@ -97,17 +97,6 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context) #region ISourceGenerator Members /// - public void Initialize(IncrementalGeneratorInitializationContext context) - { - context.RegisterPostInitializationOutput(context => - { - context.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); - context.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); - }); - } - - /// - [Obsolete] public void Execute(GeneratorExecutionContext context) { if (context.SyntaxContextReceiver is not ActorInterfaceSyntaxReceiver actorInterfaceSyntaxReceiver) @@ -168,7 +157,6 @@ namespace {namespaceName} } /// - [Obsolete] public void Initialize(GeneratorInitializationContext context) { /* @@ -283,33 +271,33 @@ returnTypeArgument is not null } } -internal static class Extensions -{ - public static int IndexOf(this IEnumerable source, Func predicate) - { - int index = 0; - - foreach (var item in source) - { - if (predicate(item)) - { - return index; - } - - index++; - } - - return -1; - } -} - -internal sealed class DiagnosticsException : Exception -{ - public DiagnosticsException(IEnumerable diagnostics) - : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) - { - this.Diagnostics = diagnostics.ToArray(); - } - - public IEnumerable Diagnostics { get; } -} +//internal static class Extensions +//{ +// public static int IndexOf(this IEnumerable source, Func predicate) +// { +// int index = 0; + +// foreach (var item in source) +// { +// if (predicate(item)) +// { +// return index; +// } + +// index++; +// } + +// return -1; +// } +//} + +//internal sealed class DiagnosticsException : Exception +//{ +// public DiagnosticsException(IEnumerable diagnostics) +// : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) +// { +// this.Diagnostics = diagnostics.ToArray(); +// } + +// public IEnumerable Diagnostics { get; } +//} diff --git a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs index ce4c0accd..d4d8cc418 100644 --- a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs +++ b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs @@ -17,7 +17,7 @@ namespace Dapr.Actors.Generators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; -using VerifyCS = CSharpSourceGeneratorVerifier; +using VerifyCS = CSharpSourceGeneratorVerifier; public sealed class ActorClientGeneratorTests { From c44384b09fba5b7c0a64caedfd7c757d401480a6 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 10:29:27 +0200 Subject: [PATCH 05/63] Debug profile added Signed-off-by: Manuel Menegazzo --- .../Dapr.Actors.Generators.csproj | 67 ++++++++++--------- .../Properties/launchSettings.json | 8 +++ 2 files changed, 42 insertions(+), 33 deletions(-) create mode 100644 src/Dapr.Actors.Generators/Properties/launchSettings.json diff --git a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj index e7f6abbe7..4c7f33934 100644 --- a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj +++ b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj @@ -1,45 +1,46 @@ - - enable - enable - - - - 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/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 From 9f9a4b71f9a806e96786c0b25ff7e3e3200ee0ac Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 12:02:19 +0200 Subject: [PATCH 06/63] Updated implementation Signed-off-by: Manuel Menegazzo --- .../ActorClient/IClientActor.cs | 2 +- .../ActorClientGenerator.cs | 143 +++++++----------- .../ActorClientGeneratorOld.cs | 31 ---- 3 files changed, 58 insertions(+), 118 deletions(-) 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 04c2e2876..7a6c88c15 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -12,7 +12,6 @@ // ------------------------------------------------------------------------ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Dapr.Actors.Generators; @@ -64,38 +63,6 @@ internal sealed class GenerateActorClientAttribute : Attribute }} }}"; - private sealed class ActorInterfaceSyntaxReceiver : ISyntaxContextReceiver - { - private readonly List models = new(); - - public IEnumerable Models => this.models; - - #region ISyntaxContextReceiver Members - - public void OnVisitSyntaxNode(GeneratorSyntaxContext 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 - } - - #region ISourceGenerator Members - /// public void Initialize(IncrementalGeneratorInitializationContext context) { @@ -104,39 +71,54 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); context.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); }); + + // Do a simple filter for enums + IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider + .ForAttributeWithMetadataName( + GenerateActorClientAttributeFullTypeName, + predicate: static (_, _) => true, + transform: static (gasc, cancellationToken) => GetSemanticTargetForGeneration(gasc, cancellationToken)) + .Where(static m => m is not null); // Filter out errors that we don't care about + + context.RegisterSourceOutput(actorClientsToGenerate, GenerateActorClientCode); } - /// - [Obsolete] - public void Execute(GeneratorExecutionContext context) + static ActorClientToGenerate? GetSemanticTargetForGeneration( + GeneratorAttributeSyntaxContext context, + CancellationToken cancellationToken) { - if (context.SyntaxContextReceiver is not ActorInterfaceSyntaxReceiver actorInterfaceSyntaxReceiver) - { - return; - } + var actorMethodAttributeSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName(ActorMethodAttributeFullTypeName) + ?? throw new InvalidOperationException("Could not find ActorMethodAttribute."); - 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."); + var cancellationTokenSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") + ?? throw new InvalidOperationException("Could not find CancellationToken."); - foreach (var interfaceSymbol in actorInterfaceSyntaxReceiver.Models) - { - try - { - var actorInterfaceTypeName = interfaceSymbol.Name; - var fullyQualifiedActorInterfaceTypeName = interfaceSymbol.ToString(); + // 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 actorInterfaceSymbol = (INamedTypeSymbol)context.TargetSymbol; + + var actorInterfaceTypeName = actorInterfaceSymbol.Name; + var fullyQualifiedActorInterfaceTypeName = actorInterfaceSymbol.ToString(); - var attributeData = interfaceSymbol.GetAttributes().Single(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); + var accessibility = GetClientAccessibility(actorInterfaceSymbol); + var clientTypeName = GetClientName(actorInterfaceSymbol, attributeData); - 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 namespaceName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Namespace").Value.Value?.ToString() + ?? actorInterfaceSymbol.ContainingNamespace.ToDisplayString(); - var members = interfaceSymbol.GetMembers().OfType().Where(m => m.MethodKind == MethodKind.Ordinary).ToList(); + var members = actorInterfaceSymbol + .GetMembers() + .OfType() + .Where(m => m.MethodKind == MethodKind.Ordinary) + .ToList(); - var methodImplementations = String.Join("\n", members.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + var methodImplementations = string.Join( + "\n", + members.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); - var source = $@" + var source = $@" // namespace {namespaceName} @@ -154,43 +136,32 @@ namespace {namespaceName} }} }} "; - // 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 ActorClientToGenerate + { + FullyQualifiedActorCleintTypeName = $"{namespaceName}.{clientTypeName}", + Source = source + }; } - /// - [Obsolete] - public void Initialize(GeneratorInitializationContext context) + static void GenerateActorClientCode(SourceProductionContext context, ActorClientToGenerate? source) { - /* - while (!Debugger.IsAttached) + if (source is null) { - System.Threading.Thread.Sleep(500); + return; } - */ - - context.RegisterForPostInitialization( - i => - { - i.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); - i.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); - }); - context.RegisterForSyntaxNotifications(() => new ActorInterfaceSyntaxReceiver()); + context.AddSource($"{source.FullyQualifiedActorCleintTypeName}.g.cs", source.Source); } - #endregion + private class ActorClientToGenerate + { + public string FullyQualifiedActorCleintTypeName { get; set; } = string.Empty; + + public string Source { get; set; } = string.Empty; + } - private static string GetClientAccessibility(INamedTypeSymbol interfaceSymbol) + private static string GetClientAccessibility(ISymbol interfaceSymbol) { return interfaceSymbol.DeclaredAccessibility switch { @@ -203,7 +174,7 @@ private static string GetClientAccessibility(INamedTypeSymbol interfaceSymbol) }; } - private static string GetClientName(INamedTypeSymbol interfaceSymbol, AttributeData attributeData) + private static string GetClientName(ISymbol interfaceSymbol, AttributeData attributeData) { string? clientName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Name").Value.Value?.ToString(); @@ -306,7 +277,7 @@ public static int IndexOf(this IEnumerable source, Func predicate internal sealed class DiagnosticsException : Exception { public DiagnosticsException(IEnumerable diagnostics) - : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) + : base(string.Join("\n", diagnostics.Select(d => d.ToString()))) { this.Diagnostics = diagnostics.ToArray(); } diff --git a/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs b/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs index 105649664..b859ed711 100644 --- a/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs +++ b/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs @@ -270,34 +270,3 @@ returnTypeArgument is not null }}"; } } - -//internal static class Extensions -//{ -// public static int IndexOf(this IEnumerable source, Func predicate) -// { -// int index = 0; - -// foreach (var item in source) -// { -// if (predicate(item)) -// { -// return index; -// } - -// index++; -// } - -// return -1; -// } -//} - -//internal sealed class DiagnosticsException : Exception -//{ -// public DiagnosticsException(IEnumerable diagnostics) -// : base(String.Join("\n", diagnostics.Select(d => d.ToString()))) -// { -// this.Diagnostics = diagnostics.ToArray(); -// } - -// public IEnumerable Diagnostics { get; } -//} From c86c7097164f90398420c1e8c7698992855b8e8e Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 15:06:13 +0200 Subject: [PATCH 07/63] Emitted DAPR001 Diagnostic warning Signed-off-by: Manuel Menegazzo --- .../ActorClient/IClientActor.cs | 12 + .../ActorClientGenerator.cs | 268 ++++++++++-------- .../ActorClientGeneratorOld.cs | 1 + .../AnalyzerReleases.Shipped.md | 7 + .../AnalyzerReleases.Unshipped.md | 3 + .../Dapr.Actors.Generators.csproj | 7 +- ...CancellationTokensMustBeTheLastArgument.cs | 26 ++ .../Extensions/IEnumerableExtensions.cs | 34 +++ 8 files changed, 242 insertions(+), 116 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/Diagnostics/CancellationTokensMustBeTheLastArgument.cs create mode 100644 src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs diff --git a/examples/GeneratedActor/ActorClient/IClientActor.cs b/examples/GeneratedActor/ActorClient/IClientActor.cs index c687ecf03..4ffdeea92 100644 --- a/examples/GeneratedActor/ActorClient/IClientActor.cs +++ b/examples/GeneratedActor/ActorClient/IClientActor.cs @@ -26,3 +26,15 @@ internal interface IClientActor [ActorMethod(Name = "SetState")] Task SetStateAsync(ClientState state, CancellationToken cancellationToken = default); } + +[GenerateActorClient] +internal interface IClientActor2 +{ + [ActorMethod(Name = "GetState")] + Task GetStateAsync(CancellationToken cancellationToken = default); + + [ActorMethod(Name = "SetState")] + Task SetStateAsync(CancellationToken cancellationToken, ClientState state); + + Task SetStateAsync(ClientState state, string test, CancellationToken cancellationToken = default); +} diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 7a6c88c15..6151f4f8d 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Dapr.Actors.Generators.Diagnostics; +using Dapr.Actors.Generators.Extensions; using Microsoft.CodeAnalysis; namespace Dapr.Actors.Generators; @@ -29,141 +31,210 @@ public sealed class ActorClientGenerator : IIncrementalGenerator private const string GenerateActorClientAttribute = "GenerateActorClientAttribute"; private const string GenerateActorClientAttributeFullTypeName = GeneratorsNamespace + "." + GenerateActorClientAttribute; - private const string ActorMethodAttributeText = $@" - // + private static string ActorMethodAttributeSourceText(string generatorNamespace) + { + if (generatorNamespace == null) + { + throw new ArgumentNullException(nameof(generatorNamespace)); + } - #nullable enable + var source = $@" +// - using System; +#nullable enable - namespace {GeneratorsNamespace} - {{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - internal sealed class ActorMethodAttribute : Attribute - {{ - public string? Name {{ get; set; }} - }} - }}"; +using System; - private const string GenerateActorClientAttributeText = $@" - // +namespace {generatorNamespace} +{{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + internal sealed class ActorMethodAttribute : Attribute + {{ + public string? Name {{ get; set; }} + }} +}}"; - #nullable enable + return source; + } + private static string GenerateActorClientAttributeSourceText(string generatorNamespace) + { + if (generatorNamespace == null) + { + throw new ArgumentNullException(nameof(generatorNamespace)); + } - using System; + string source = $@" +// - namespace {GeneratorsNamespace} - {{ - [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - internal sealed class GenerateActorClientAttribute : Attribute - {{ - public string? Name {{ get; set; }} +#nullable enable - public string? Namespace {{ get; set; }} - }} - }}"; +using System; + +namespace {generatorNamespace} +{{ + [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class GenerateActorClientAttribute : Attribute + {{ + public string? Name {{ get; set; }} + + public string? Namespace {{ get; set; }} + }} +}}"; + + return source; + } /// public void Initialize(IncrementalGeneratorInitializationContext context) { context.RegisterPostInitializationOutput(context => { - context.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); - context.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); + context.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeSourceText(GeneratorsNamespace)); + context.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeSourceText(GeneratorsNamespace)); }); - // Do a simple filter for enums - IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider + // Register the attribute that triggers the generation of actor clients. + IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider .ForAttributeWithMetadataName( GenerateActorClientAttributeFullTypeName, predicate: static (_, _) => true, - transform: static (gasc, cancellationToken) => GetSemanticTargetForGeneration(gasc, cancellationToken)) - .Where(static m => m is not null); // Filter out errors that we don't care about + transform: static (gasc, cancellationToken) => GetActorClientDescription(gasc, cancellationToken)); context.RegisterSourceOutput(actorClientsToGenerate, GenerateActorClientCode); } - static ActorClientToGenerate? GetSemanticTargetForGeneration( + /// + /// Returns the descriptor for the actor client to generate. + /// + /// + /// + /// + /// + static ActorClientDescriptor? GetActorClientDescription( GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { - var actorMethodAttributeSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName(ActorMethodAttributeFullTypeName) - ?? throw new InvalidOperationException("Could not find ActorMethodAttribute."); - - var cancellationTokenSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") - ?? throw new InvalidOperationException("Could not find CancellationToken."); - // 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 actorInterfaceSymbol = (INamedTypeSymbol)context.TargetSymbol; - var actorInterfaceTypeName = actorInterfaceSymbol.Name; - var fullyQualifiedActorInterfaceTypeName = actorInterfaceSymbol.ToString(); - - var accessibility = GetClientAccessibility(actorInterfaceSymbol); - var clientTypeName = GetClientName(actorInterfaceSymbol, attributeData); - + // Use the namespace specified in the attribute, 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(); + // Use the name specified in the attribute, 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"; + var members = actorInterfaceSymbol .GetMembers() .OfType() .Where(m => m.MethodKind == MethodKind.Ordinary) .ToList(); - var methodImplementations = string.Join( - "\n", - members.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + return new ActorClientDescriptor + { + NamespaceName = namespaceName, + ClientTypeName = GetClientName(actorInterfaceSymbol, attributeData), + Methods = members, + Accessibility = actorInterfaceSymbol.DeclaredAccessibility, + InterfaceType = actorInterfaceSymbol, + Compilation = context.SemanticModel.Compilation, + }; + } - var source = $@" + static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor? descriptor) + { + if (descriptor is null) + { + return; + } + + try + { + var actorMethodAttributeSymbol = descriptor.Compilation.GetTypeByMetadataName(ActorMethodAttributeFullTypeName) + ?? throw new InvalidOperationException("Could not find ActorMethodAttribute."); + + var cancellationTokenSymbol = descriptor.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") + ?? throw new InvalidOperationException("Could not find CancellationToken."); + + var methodImplementations = string.Join( + "\n", + descriptor.Methods.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + + var source = $@" // -namespace {namespaceName} +namespace {descriptor.NamespaceName} {{ - {accessibility} sealed class {clientTypeName} : {fullyQualifiedActorInterfaceTypeName} + {GetTextAccessibility(descriptor.Accessibility)} sealed class {descriptor.ClientTypeName} : {descriptor.InterfaceType.ToString()} {{ private readonly Dapr.Actors.Client.ActorProxy actorProxy; - public {clientTypeName}(Dapr.Actors.Client.ActorProxy actorProxy) + public {descriptor.ClientTypeName}(Dapr.Actors.Client.ActorProxy actorProxy) {{ this.actorProxy = actorProxy; }} {methodImplementations} }} -}} -"; - - return new ActorClientToGenerate - { - FullyQualifiedActorCleintTypeName = $"{namespaceName}.{clientTypeName}", - Source = source - }; - } +}}"; - static void GenerateActorClientCode(SourceProductionContext context, ActorClientToGenerate? source) - { - if (source is null) + context.AddSource($"{descriptor.FullyQualifiedTypeName}.g.cs", source); + } + catch (DiagnosticsException e) { - return; + foreach (var diagnostic in e.Diagnostics) + { + context.ReportDiagnostic(diagnostic); + } } - - context.AddSource($"{source.FullyQualifiedActorCleintTypeName}.g.cs", source.Source); } - private class ActorClientToGenerate + /// + /// Describes an actor client to generate. + /// + private class ActorClientDescriptor { - public string FullyQualifiedActorCleintTypeName { get; set; } = string.Empty; - - public string Source { get; set; } = string.Empty; + /// + /// Gets the actor interface symbol. + /// + 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 for the client. + /// + public IEnumerable Methods { get; set; } = Array.Empty(); + + public Compilation Compilation { get; set; } = null!; } - private static string GetClientAccessibility(ISymbol interfaceSymbol) + // TODO: check there is a better way to get the accessibility + private static string GetTextAccessibility(Accessibility accessibility) { - return interfaceSymbol.DeclaredAccessibility switch + return accessibility switch { Accessibility.Public => "public", Accessibility.Internal => "internal", @@ -174,16 +245,10 @@ private static string GetClientAccessibility(ISymbol interfaceSymbol) }; } - private static string GetClientName(ISymbol 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; - } - - private static string GenerateMethodImplementation(IMethodSymbol method, INamedTypeSymbol generateActorClientAttributeSymbol, INamedTypeSymbol cancellationTokenSymbol) + private static string 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; @@ -192,27 +257,19 @@ private static string GenerateMethodImplementation(IMethodSymbol method, INamedT { 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()) + // TODO: improve description of exception with method name and parameter name + 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.", + "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, @@ -221,7 +278,8 @@ private static string GenerateMethodImplementation(IMethodSymbol method, INamedT }); } - var attributeData = method.GetAttributes().SingleOrDefault(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); + 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; @@ -254,26 +312,6 @@ returnTypeArgument is not null } } -internal static class Extensions -{ - public static int IndexOf(this IEnumerable source, Func predicate) - { - int index = 0; - - foreach (var item in source) - { - if (predicate(item)) - { - return index; - } - - index++; - } - - return -1; - } -} - internal sealed class DiagnosticsException : Exception { public DiagnosticsException(IEnumerable diagnostics) diff --git a/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs b/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs index b859ed711..b1a38a8cd 100644 --- a/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs +++ b/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Dapr.Actors.Generators.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md b/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md new file mode 100644 index 000000000..44aa83255 --- /dev/null +++ b/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md @@ -0,0 +1,7 @@ +## Release 1.14 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|-------------------- +DAPR0001| Usage | Error | Cancellation tokens must be the last argument \ 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/Dapr.Actors.Generators.csproj b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj index 4c7f33934..d898b8c5b 100644 --- a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj +++ b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj @@ -1,4 +1,4 @@ - + enable @@ -43,4 +43,9 @@ + + + + + diff --git a/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs new file mode 100644 index 000000000..9d9a4c6a5 --- /dev/null +++ b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs @@ -0,0 +1,26 @@ +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, string reason) => Diagnostic.Create( + Rule, + symbol.Locations.First(), + symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + reason); + } +} diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs new file mode 100644 index 000000000..bfaef6ee8 --- /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. + /// + /// + /// + /// + /// Return the zero-based index of the first occurrence of an element that satisfies the condition, if found; otherwise, -1. + public 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; + } + } +} From 38f4fc17f77a6ec5e2c37925369d5907a1b876f5 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 15:26:32 +0200 Subject: [PATCH 08/63] Added DAPR002 diagnostic Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 28 +++++-------------- .../AnalyzerReleases.Shipped.md | 3 +- ...CancellationTokensMustBeTheLastArgument.cs | 5 ++-- ...tOptionallyFollowedByACancellationToken.cs | 25 +++++++++++++++++ 4 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 src/Dapr.Actors.Generators/Diagnostics/MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.cs diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 6151f4f8d..a43444b5c 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -137,7 +137,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return new ActorClientDescriptor { NamespaceName = namespaceName, - ClientTypeName = GetClientName(actorInterfaceSymbol, attributeData), + ClientTypeName = clientName, Methods = members, Accessibility = actorInterfaceSymbol.DeclaredAccessibility, InterfaceType = actorInterfaceSymbol, @@ -162,7 +162,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var methodImplementations = string.Join( "\n", - descriptor.Methods.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + descriptor.Methods.Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol))); var source = $@" // @@ -246,6 +246,7 @@ private static string GetTextAccessibility(Accessibility accessibility) } private static string GenerateMethodImplementation( + SourceProductionContext context, IMethodSymbol method, INamedTypeSymbol generateActorClientAttributeSymbol, INamedTypeSymbol cancellationTokenSymbol) @@ -255,27 +256,12 @@ private static string GenerateMethodImplementation( if (cancellationTokenParameter is not null && cancellationTokenIndex != method.Parameters.Length - 1) { - throw new DiagnosticsException(new[] - { - // TODO: improve description of exception with method name and parameter name - CancellationTokensMustBeTheLastArgument.CreateDiagnostic(cancellationTokenParameter, "") - }); + context.ReportDiagnostic(CancellationTokensMustBeTheLastArgument.CreateDiagnostic(cancellationTokenParameter)); } 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()) - }); + context.ReportDiagnostic(MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.CreateDiagnostic(method)); } var attributeData = method.GetAttributes() @@ -287,7 +273,7 @@ private static string GenerateMethodImplementation( var returnTypeArgument = (method.ReturnType as INamedTypeSymbol)?.TypeArguments.FirstOrDefault(); - string argumentDefinitions = String.Join(", ", method.Parameters.Select(p => $"{p.Type} {p.Name}")); + string argumentDefinitions = string.Join(", ", method.Parameters.Select(p => $"{p.Type} {p.Name}")); if (cancellationTokenParameter is not null && cancellationTokenParameter.IsOptional @@ -297,7 +283,7 @@ private static string GenerateMethodImplementation( argumentDefinitions = argumentDefinitions + " = default"; } - string argumentList = String.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); + string argumentList = string.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); string templateArgs = returnTypeArgument is not null diff --git a/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md b/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md index 44aa83255..62b61ac2c 100644 --- a/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md +++ b/src/Dapr.Actors.Generators/AnalyzerReleases.Shipped.md @@ -4,4 +4,5 @@ Rule ID | Category | Severity | Notes --------|----------|----------|-------------------- -DAPR0001| Usage | Error | Cancellation tokens must be the last argument \ No newline at end of file +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/Diagnostics/CancellationTokensMustBeTheLastArgument.cs b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs index 9d9a4c6a5..376bb360f 100644 --- a/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs +++ b/src/Dapr.Actors.Generators/Diagnostics/CancellationTokensMustBeTheLastArgument.cs @@ -17,10 +17,9 @@ internal static class CancellationTokensMustBeTheLastArgument DiagnosticSeverity.Error, isEnabledByDefault: true); - internal static Diagnostic CreateDiagnostic(ISymbol symbol, string reason) => Diagnostic.Create( + internal static Diagnostic CreateDiagnostic(ISymbol symbol) => Diagnostic.Create( Rule, symbol.Locations.First(), - symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - reason); + 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)); + } +} From 8611f6778baee9aa36c2af6477e09f2862833ff4 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 15:30:50 +0200 Subject: [PATCH 09/63] Cleaun Signed-off-by: Manuel Menegazzo --- examples/GeneratedActor/ActorClient/IClientActor.cs | 12 ------------ src/Dapr.Actors.Generators/ActorClientGenerator.cs | 5 ++++- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/examples/GeneratedActor/ActorClient/IClientActor.cs b/examples/GeneratedActor/ActorClient/IClientActor.cs index 4ffdeea92..c687ecf03 100644 --- a/examples/GeneratedActor/ActorClient/IClientActor.cs +++ b/examples/GeneratedActor/ActorClient/IClientActor.cs @@ -26,15 +26,3 @@ internal interface IClientActor [ActorMethod(Name = "SetState")] Task SetStateAsync(ClientState state, CancellationToken cancellationToken = default); } - -[GenerateActorClient] -internal interface IClientActor2 -{ - [ActorMethod(Name = "GetState")] - Task GetStateAsync(CancellationToken cancellationToken = default); - - [ActorMethod(Name = "SetState")] - Task SetStateAsync(CancellationToken cancellationToken, ClientState state); - - Task SetStateAsync(ClientState state, string test, CancellationToken cancellationToken = default); -} diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index a43444b5c..c52d03ba5 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -224,10 +224,13 @@ private class ActorClientDescriptor public string FullyQualifiedTypeName => $"{NamespaceName}.{ClientTypeName}"; /// - /// Methods to generate for the client. + /// Methods to generate in the client. /// public IEnumerable Methods { get; set; } = Array.Empty(); + /// + /// Compilation to use for generating the client. + /// public Compilation Compilation { get; set; } = null!; } From 56b9ab9e37a5e90d11849ad345d7dd1d8a4d882a Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 17:30:49 +0200 Subject: [PATCH 10/63] UP Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index c52d03ba5..8cf262a8a 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -11,9 +11,12 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Collections.Immutable; using Dapr.Actors.Generators.Diagnostics; using Dapr.Actors.Generators.Extensions; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Dapr.Actors.Generators; @@ -162,7 +165,28 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var methodImplementations = string.Join( "\n", - descriptor.Methods.Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + descriptor.Methods + .OrderBy(member => member.DeclaredAccessibility) + .ThenBy(member => member.Name) + .Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol))); + + var t = descriptor.Methods.FirstOrDefault(); + + var actorClientClassModifiers = new List() + .Concat(new[] { SyntaxKind.SealedKeyword }) + .Concat(GetSyntaxKinds(descriptor.Accessibility)) + .Select(sk => SyntaxFactory.Token(sk)); + + var actorClientClassDeclaration = SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName) + .WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers)); + + var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(descriptor.NamespaceName)) + .WithMembers(SyntaxFactory.List(new[] { actorClientClassDeclaration })); + + var cu = SyntaxFactory.CompilationUnit() + .WithMembers(SyntaxFactory.SingletonList(namespaceDeclaration)) + .NormalizeWhitespace() + .ToFullString(); var source = $@" // @@ -193,10 +217,11 @@ namespace {descriptor.NamespaceName} } } + /// /// Describes an actor client to generate. /// - private class ActorClientDescriptor + private record class ActorClientDescriptor : IEquatable { /// /// Gets the actor interface symbol. @@ -234,6 +259,35 @@ private class ActorClientDescriptor public Compilation Compilation { get; set; } = null!; } + private static IEnumerable 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; + } + // TODO: check there is a better way to get the accessibility private static string GetTextAccessibility(Accessibility accessibility) { From a2f36163eb072372d35d448dc0b5e95443066765 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 17:31:25 +0200 Subject: [PATCH 11/63] Added summaries Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 8cf262a8a..4137ae705 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -259,6 +259,12 @@ private record class ActorClientDescriptor : IEquatable public Compilation Compilation { get; set; } = null!; } + /// + /// Returns the syntax kinds for the specified accessibility. + /// + /// + /// + /// private static IEnumerable GetSyntaxKinds(Accessibility accessibility) { var syntaxKinds = new List(); From b2deca71e6e26afd643e27cb47c7a0c060327d26 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 21:38:43 +0200 Subject: [PATCH 12/63] Added base interface to ActorClient Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 4137ae705..c9e52e6c4 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -172,18 +172,24 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var t = descriptor.Methods.FirstOrDefault(); + var actorClientBaseInterface = SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(descriptor.InterfaceType.ToString())); + var autoGeneratedComment = SyntaxFactory.Comment("// "); var actorClientClassModifiers = new List() .Concat(new[] { SyntaxKind.SealedKeyword }) .Concat(GetSyntaxKinds(descriptor.Accessibility)) .Select(sk => SyntaxFactory.Token(sk)); var actorClientClassDeclaration = SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName) - .WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers)); + .WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers)) + .WithBaseList(SyntaxFactory.BaseList( + SyntaxFactory.Token(SyntaxKind.ColonToken), + SyntaxFactory.SeparatedList(new[] { actorClientBaseInterface }))); var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(descriptor.NamespaceName)) - .WithMembers(SyntaxFactory.List(new[] { actorClientClassDeclaration })); + .WithMembers(SyntaxFactory.List(new[] { actorClientClassDeclaration })) + .WithLeadingTrivia(SyntaxFactory.TriviaList(new[] { autoGeneratedComment })); - var cu = SyntaxFactory.CompilationUnit() + var compilationUnit = SyntaxFactory.CompilationUnit() .WithMembers(SyntaxFactory.SingletonList(namespaceDeclaration)) .NormalizeWhitespace() .ToFullString(); From 45ef4f6a07f2fb0a8c636a63f23b299955c90c28 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 23:14:08 +0200 Subject: [PATCH 13/63] Updated Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 63 ++++++++++--------- .../Extensions/IEnumerableExtensions.cs | 14 +++++ 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index c9e52e6c4..8cfb75d54 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -163,56 +163,57 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var cancellationTokenSymbol = descriptor.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") ?? throw new InvalidOperationException("Could not find CancellationToken."); - var methodImplementations = string.Join( - "\n", - descriptor.Methods - .OrderBy(member => member.DeclaredAccessibility) - .ThenBy(member => member.Name) - .Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol))); - - var t = descriptor.Methods.FirstOrDefault(); - var actorClientBaseInterface = SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(descriptor.InterfaceType.ToString())); var autoGeneratedComment = SyntaxFactory.Comment("// "); + var actorClientClassModifiers = new List() - .Concat(new[] { SyntaxKind.SealedKeyword }) .Concat(GetSyntaxKinds(descriptor.Accessibility)) + .Concat(SyntaxKind.SealedKeyword) .Select(sk => SyntaxFactory.Token(sk)); + 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(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) + }))) + .WithBody(default); + + var actorMethods = descriptor.Methods + .OrderBy(member => member.DeclaredAccessibility) + .ThenBy(member => member.Name) + .Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol)) + .Select(m => SyntaxFactory.ParseMemberDeclaration(m)!); + + var actorField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) + .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))); + + var actorMembers = new List() + .Concat(actorField) + .Concat(actorCtor) + .Concat(actorMethods) + .ToList(); + 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 })); + .WithLeadingTrivia(SyntaxFactory.TriviaList(new[] { + autoGeneratedComment, + })); - var compilationUnit = SyntaxFactory.CompilationUnit() + var compilationOutput = SyntaxFactory.CompilationUnit() .WithMembers(SyntaxFactory.SingletonList(namespaceDeclaration)) .NormalizeWhitespace() .ToFullString(); - var source = $@" -// - -namespace {descriptor.NamespaceName} -{{ - {GetTextAccessibility(descriptor.Accessibility)} sealed class {descriptor.ClientTypeName} : {descriptor.InterfaceType.ToString()} - {{ - private readonly Dapr.Actors.Client.ActorProxy actorProxy; - - public {descriptor.ClientTypeName}(Dapr.Actors.Client.ActorProxy actorProxy) - {{ - this.actorProxy = actorProxy; - }} - - {methodImplementations} - }} -}}"; - - context.AddSource($"{descriptor.FullyQualifiedTypeName}.g.cs", source); + context.AddSource($"{descriptor.FullyQualifiedTypeName}.g.cs", compilationOutput); } catch (DiagnosticsException e) { diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index bfaef6ee8..e52d07032 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -30,5 +30,19 @@ public static int IndexOf(this IEnumerable source, Func predicate return -1; } + + /// + /// Concatenates the specified item to the end of the sequence. + /// + /// + /// + /// + /// + public static IEnumerable Concat(this IEnumerable source, T item) + { + source.Concat(new[] { item }); + + return source; + } } } From a756015382efffb052b428e2d1b5ea419e3a9782 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 9 Jul 2024 23:30:41 +0200 Subject: [PATCH 14/63] Added ctor Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 15 +++++++++++---- .../Extensions/IEnumerableExtensions.cs | 4 +--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 8cfb75d54..5b783f269 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -177,7 +177,13 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient { SyntaxFactory.Parameter(SyntaxFactory.Identifier("actorProxy")).WithType(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) }))) - .WithBody(default); + .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new[] + { + SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName("this.actorProxy"), + SyntaxFactory.IdentifierName("actorProxy")) + ) + }))); var actorMethods = descriptor.Methods .OrderBy(member => member.DeclaredAccessibility) @@ -185,12 +191,13 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient .Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol)) .Select(m => SyntaxFactory.ParseMemberDeclaration(m)!); - var actorField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) + var actorProxyField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) - .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))); + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))) + .WithLeadingTrivia(SyntaxFactory.TriviaList(new[] { SyntaxFactory.CarriageReturnLineFeed })); var actorMembers = new List() - .Concat(actorField) + .Concat(actorProxyField) .Concat(actorCtor) .Concat(actorMethods) .ToList(); diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index e52d07032..147f326d2 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -40,9 +40,7 @@ public static int IndexOf(this IEnumerable source, Func predicate /// public static IEnumerable Concat(this IEnumerable source, T item) { - source.Concat(new[] { item }); - - return source; + return source.Concat(new[] { item }); } } } From 28db658df2be51b77d2b97f008c70e26a7b0b7ca Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 10 Jul 2024 16:24:04 +0200 Subject: [PATCH 15/63] Added nullable directive Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 30 +++++++++++++------ .../Helpers/ExceptionHelpers.cs | 30 +++++++++++++++++++ 2 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 5b783f269..5fa5c77eb 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -59,6 +59,7 @@ internal sealed class ActorMethodAttribute : Attribute return source; } + private static string GenerateActorClientAttributeSourceText(string generatorNamespace) { if (generatorNamespace == null) @@ -165,24 +166,40 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient 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 actorClientClassModifiers = new List() .Concat(GetSyntaxKinds(descriptor.Accessibility)) .Concat(SyntaxKind.SealedKeyword) .Select(sk => SyntaxFactory.Token(sk)); + var actorProxyField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) + .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) + .WithModifiers(SyntaxFactory.TokenList(new[] + { + SyntaxFactory.Token(SyntaxKind.PrivateKeyword), + SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword) + })); + 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(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) }))) - .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new[] + .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new[] { - SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + //SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ArgumentNullExceptionSyntax("actorProxy")), + //SyntaxFactory.ExpressionStatement(SyntaxFactory.BinaryExpression( + // SyntaxKind.IsExpression, + // SyntaxFactory.IdentifierName("actorProxy"), + // SyntaxFactory.(SyntaxKind.NullableKeyword) + //)), + SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName("this.actorProxy"), SyntaxFactory.IdentifierName("actorProxy")) - ) + ), }))); var actorMethods = descriptor.Methods @@ -191,11 +208,6 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient .Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol)) .Select(m => SyntaxFactory.ParseMemberDeclaration(m)!); - var actorProxyField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) - .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) - .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))) - .WithLeadingTrivia(SyntaxFactory.TriviaList(new[] { SyntaxFactory.CarriageReturnLineFeed })); - var actorMembers = new List() .Concat(actorProxyField) .Concat(actorCtor) @@ -213,6 +225,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient .WithMembers(SyntaxFactory.List(new[] { actorClientClassDeclaration })) .WithLeadingTrivia(SyntaxFactory.TriviaList(new[] { autoGeneratedComment, + nullableAnnotation, })); var compilationOutput = SyntaxFactory.CompilationUnit() @@ -231,7 +244,6 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient } } - /// /// Describes an actor client to generate. /// diff --git a/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs b/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs new file mode 100644 index 000000000..0343b2887 --- /dev/null +++ b/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs @@ -0,0 +1,30 @@ +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Dapr.Actors.Generators.Helpers +{ + /// + /// Syntax factory helpers for generating exception syntax. + /// + public static partial class SyntaxFactoryHelpers + { + /// + /// Generates a syntax for ArgumentNullException syntax for the given parameter name. + /// + /// + /// + public static ThrowExpressionSyntax ArgumentNullExceptionSyntax(string parameterName) + { + return SyntaxFactory.ThrowExpression( + SyntaxFactory.Token(SyntaxKind.ThrowKeyword), + SyntaxFactory.InvocationExpression( + SyntaxFactory.ParseTypeName("System.ArgumentNullException"), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] + { + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameterName)) + })) + ) + ); + } + } +} From 4be5bb7575146422f43012d41138c12d0793d74c Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 10 Jul 2024 16:45:10 +0200 Subject: [PATCH 16/63] Added null check for actorproxy ctor parameter Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 5fa5c77eb..9145fd079 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -14,6 +14,7 @@ using System.Collections.Immutable; using Dapr.Actors.Generators.Diagnostics; using Dapr.Actors.Generators.Extensions; +using Dapr.Actors.Generators.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -187,14 +188,16 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient { SyntaxFactory.Parameter(SyntaxFactory.Identifier("actorProxy")).WithType(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) }))) - .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new[] + .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { - //SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ArgumentNullExceptionSyntax("actorProxy")), - //SyntaxFactory.ExpressionStatement(SyntaxFactory.BinaryExpression( - // SyntaxKind.IsExpression, - // SyntaxFactory.IdentifierName("actorProxy"), - // SyntaxFactory.(SyntaxKind.NullableKeyword) - //)), + SyntaxFactory.IfStatement( + SyntaxFactory.BinaryExpression( + SyntaxKind.IsExpression, + SyntaxFactory.IdentifierName("actorProxy"), + SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression) + ), + SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ArgumentNullExceptionSyntax("actorProxy")) + ), SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName("this.actorProxy"), From c24d310a842094a1647cac9f3d7ad9846912343b Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 10 Jul 2024 21:59:00 +0200 Subject: [PATCH 17/63] Moved DiagnoticException in a dedicate cs file Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 13 +------------ .../DiagnosticsException.cs | 15 +++++++++++++++ .../Helpers/ExceptionHelpers.cs | 8 ++++---- 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 src/Dapr.Actors.Generators/DiagnosticsException.cs diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 9145fd079..f3a53bc46 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -188,7 +188,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient { SyntaxFactory.Parameter(SyntaxFactory.Identifier("actorProxy")).WithType(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) }))) - .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] + .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { SyntaxFactory.IfStatement( SyntaxFactory.BinaryExpression( @@ -389,14 +389,3 @@ returnTypeArgument is not null }}"; } } - -internal sealed class DiagnosticsException : Exception -{ - public DiagnosticsException(IEnumerable diagnostics) - : base(string.Join("\n", diagnostics.Select(d => d.ToString()))) - { - this.Diagnostics = diagnostics.ToArray(); - } - - public IEnumerable Diagnostics { get; } -} diff --git a/src/Dapr.Actors.Generators/DiagnosticsException.cs b/src/Dapr.Actors.Generators/DiagnosticsException.cs new file mode 100644 index 000000000..35d5b9c6c --- /dev/null +++ b/src/Dapr.Actors.Generators/DiagnosticsException.cs @@ -0,0 +1,15 @@ +using Microsoft.CodeAnalysis; + +namespace Dapr.Actors.Generators +{ + internal sealed class DiagnosticsException : Exception + { + public DiagnosticsException(IEnumerable diagnostics) + : base(string.Join("\n", diagnostics.Select(d => d.ToString()))) + { + this.Diagnostics = diagnostics.ToArray(); + } + + public IEnumerable Diagnostics { get; } + } +} diff --git a/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs b/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs index 0343b2887..ce4e446a2 100644 --- a/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs @@ -9,11 +9,11 @@ namespace Dapr.Actors.Generators.Helpers public static partial class SyntaxFactoryHelpers { /// - /// Generates a syntax for ArgumentNullException syntax for the given parameter name. + /// Generates a syntax for syntax for the given argument name. /// - /// + /// /// - public static ThrowExpressionSyntax ArgumentNullExceptionSyntax(string parameterName) + public static ThrowExpressionSyntax ArgumentNullExceptionSyntax(string argumentName) { return SyntaxFactory.ThrowExpression( SyntaxFactory.Token(SyntaxKind.ThrowKeyword), @@ -21,7 +21,7 @@ public static ThrowExpressionSyntax ArgumentNullExceptionSyntax(string parameter SyntaxFactory.ParseTypeName("System.ArgumentNullException"), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { - SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameterName)) + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName)) })) ) ); From 28fc7fe81f36085a206e314dad81d97d6dc4cf8d Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 10 Jul 2024 22:27:00 +0200 Subject: [PATCH 18/63] Moved generator costants to dedicated class Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 61 +++++++++---------- .../GeneratorConstants.cs | 33 ++++++++++ ...tionHelpers.cs => SyntaxFactoryHelpers.cs} | 0 3 files changed, 61 insertions(+), 33 deletions(-) create mode 100644 src/Dapr.Actors.Generators/GeneratorConstants.cs rename src/Dapr.Actors.Generators/Helpers/{ExceptionHelpers.cs => SyntaxFactoryHelpers.cs} (100%) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index f3a53bc46..ccee0e089 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -12,12 +12,14 @@ // ------------------------------------------------------------------------ using System.Collections.Immutable; +using System.Text; using Dapr.Actors.Generators.Diagnostics; using Dapr.Actors.Generators.Extensions; using Dapr.Actors.Generators.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; namespace Dapr.Actors.Generators; @@ -27,15 +29,7 @@ namespace Dapr.Actors.Generators; [Generator] 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 static string ActorMethodAttributeSourceText(string generatorNamespace) + private static SourceText ActorMethodAttributeSourceText(string generatorNamespace) { if (generatorNamespace == null) { @@ -52,16 +46,20 @@ private static string ActorMethodAttributeSourceText(string generatorNamespace) namespace {generatorNamespace} {{ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - internal sealed class ActorMethodAttribute : Attribute + internal sealed class {GeneratorConstants.ActorMethodAttributeTypeName} : Attribute {{ public string? Name {{ get; set; }} }} }}"; - return source; + return SourceText.From( + SyntaxFactory.ParseCompilationUnit(source) + .NormalizeWhitespace() + .ToFullString(), + Encoding.UTF8); } - private static string GenerateActorClientAttributeSourceText(string generatorNamespace) + private static SourceText GenerateActorClientAttributeSourceText(string generatorNamespace) { if (generatorNamespace == null) { @@ -78,7 +76,7 @@ private static string GenerateActorClientAttributeSourceText(string generatorNam namespace {generatorNamespace} {{ [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - internal sealed class GenerateActorClientAttribute : Attribute + internal sealed class {GeneratorConstants.GenerateActorClientAttributeTypeName} : Attribute {{ public string? Name {{ get; set; }} @@ -86,25 +84,36 @@ internal sealed class GenerateActorClientAttribute : Attribute }} }}"; - return source; + return SourceText.From( + SyntaxFactory.ParseCompilationUnit(source) + .NormalizeWhitespace() + .ToFullString(), + Encoding.UTF8); } /// public void Initialize(IncrementalGeneratorInitializationContext context) { + // Register the source output that generates the attribute definitions for ActorMethodAttribute and GenerateActorClientAttribute. context.RegisterPostInitializationOutput(context => { - context.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeSourceText(GeneratorsNamespace)); - context.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeSourceText(GeneratorsNamespace)); + context.AddSource( + $"{GeneratorConstants.ActorMethodAttributeFullTypeName}.g.cs", + ActorMethodAttributeSourceText(GeneratorConstants.GeneratorsNamespace)); + + context.AddSource( + $"{GeneratorConstants.GenerateActorClientAttributeFullTypeName}.g.cs", + GenerateActorClientAttributeSourceText(GeneratorConstants.GeneratorsNamespace)); }); - // Register the attribute that triggers the generation of actor clients. + // Register the value provider that triggers the generation of actor clients based on the GenerateActorClientAttribute. IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider .ForAttributeWithMetadataName( - GenerateActorClientAttributeFullTypeName, + GeneratorConstants.GenerateActorClientAttributeFullTypeName, predicate: static (_, _) => true, transform: static (gasc, cancellationToken) => GetActorClientDescription(gasc, cancellationToken)); + // Register the source output that generates the actor clients. context.RegisterSourceOutput(actorClientsToGenerate, GenerateActorClientCode); } @@ -159,7 +168,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient try { - var actorMethodAttributeSymbol = descriptor.Compilation.GetTypeByMetadataName(ActorMethodAttributeFullTypeName) + var actorMethodAttributeSymbol = descriptor.Compilation.GetTypeByMetadataName(GeneratorConstants.ActorMethodAttributeFullTypeName) ?? throw new InvalidOperationException("Could not find ActorMethodAttribute."); var cancellationTokenSymbol = descriptor.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") @@ -323,20 +332,6 @@ private static IEnumerable GetSyntaxKinds(Accessibility accessibilit return syntaxKinds; } - // TODO: check there is a better way to get the accessibility - private static string GetTextAccessibility(Accessibility accessibility) - { - return accessibility switch - { - Accessibility.Public => "public", - Accessibility.Internal => "internal", - Accessibility.Private => "private", - Accessibility.Protected => "protected", - Accessibility.ProtectedAndInternal => "protected internal", - _ => throw new InvalidOperationException("Unexpected accessibility.") - }; - } - private static string GenerateMethodImplementation( SourceProductionContext context, IMethodSymbol method, diff --git a/src/Dapr.Actors.Generators/GeneratorConstants.cs b/src/Dapr.Actors.Generators/GeneratorConstants.cs new file mode 100644 index 000000000..aa57cf120 --- /dev/null +++ b/src/Dapr.Actors.Generators/GeneratorConstants.cs @@ -0,0 +1,33 @@ +namespace Dapr.Actors.Generators +{ + /// + /// Constants used by the code generator. + /// + internal static class GeneratorConstants + { + /// + /// 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; + } +} diff --git a/src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs similarity index 100% rename from src/Dapr.Actors.Generators/Helpers/ExceptionHelpers.cs rename to src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs From aa4c1d4b9a35ede11ebf4188d29059b18e8637d2 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Wed, 26 Jun 2024 23:37:19 +0200 Subject: [PATCH 19/63] 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 --- src/Dapr.Actors/ActorReference.cs | 30 ++++--- test/Dapr.Actors.Test/ActorReferenceTests.cs | 93 ++++++++++++++++++++ 2 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 test/Dapr.Actors.Test/ActorReferenceTests.cs diff --git a/src/Dapr.Actors/ActorReference.cs b/src/Dapr.Actors/ActorReference.cs index 0e199c3ae..d72b6676f 100644 --- a/src/Dapr.Actors/ActorReference.cs +++ b/src/Dapr.Actors/ActorReference.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. @@ -16,6 +16,7 @@ namespace Dapr.Actors using System; using System.Runtime.Serialization; using Dapr.Actors.Client; + using Dapr.Actors.Runtime; /// /// Encapsulation of a reference to an actor for serialization. @@ -69,23 +70,28 @@ public object Bind(Type actorInterfaceType) private static ActorReference GetActorReference(object actor) { - if (actor == null) - { - throw new ArgumentNullException("actor"); - } + ArgumentNullException.ThrowIfNull(actor, nameof(actor)); - // try as IActorProxy for backward compatibility as customers's mock framework may rely on it before V2 remoting stack. - if (actor is IActorProxy actorProxy) + var actorReference = actor switch { - return new ActorReference() + // try as IActorProxy for backward compatibility as customers's mock framework may rely on it before V2 remoting stack. + IActorProxy actorProxy => new ActorReference() { ActorId = actorProxy.ActorId, ActorType = actorProxy.ActorType, - }; - } + }, + // Handle case when we want to get ActorReference inside the Actor implementation, + // we gather actor id and actor type from Actor base class. + Actor actorBase => new ActorReference() + { + ActorId = actorBase.Id, + ActorType = actorBase.Host.ActorTypeInfo.ActorTypeName, + }, + // Handle case when we can't cast to IActorProxy or Actor. + _ => throw new ArgumentOutOfRangeException("actor", "Invalid actor object type."), + }; - // TODO check for ActorBase - throw new ArgumentOutOfRangeException("actor"); + return actorReference; } } } diff --git a/test/Dapr.Actors.Test/ActorReferenceTests.cs b/test/Dapr.Actors.Test/ActorReferenceTests.cs new file mode 100644 index 000000000..7450f616c --- /dev/null +++ b/test/Dapr.Actors.Test/ActorReferenceTests.cs @@ -0,0 +1,93 @@ +using System; +using System.Threading.Tasks; +using Dapr.Actors.Client; +using Dapr.Actors.Runtime; +using Dapr.Actors.Test; +using Xunit; + +namespace Dapr.Actors +{ + public class ActorReferenceTests + { + [Fact] + public void Get_WhenActorIsNull_ReturnsNull() + { + // Arrange + object actor = null; + + // Act + var result = ActorReference.Get(actor); + + // Assert + Assert.Null(result); + } + + [Fact] + public void Get_FromActorProxy_ReturnsActorReference() + { + // Arrange + var expectedActorId = new ActorId("abc"); + var expectedActorType = "TestActor"; + var proxy = ActorProxy.Create(expectedActorId, typeof(ITestActor), expectedActorType); + + // Act + var actorReference = ActorReference.Get(proxy); + + // Assert + Assert.NotNull(actorReference); + Assert.Equal(expectedActorId, actorReference.ActorId); + Assert.Equal(expectedActorType, actorReference.ActorType); + } + + [Fact] + public async Task Get_FromActorImplementation_ReturnsActorReference() + { + // Arrange + var expectedActorId = new ActorId("abc"); + var expectedActorType = nameof(ActorReferenceTestActor); + var host = ActorHost.CreateForTest(new ActorTestOptions() { ActorId = expectedActorId }); + var actor = new ActorReferenceTestActor(host); + + // Act + var actorReference = await actor.GetActorReference(); + + // Assert + Assert.NotNull(actorReference); + Assert.Equal(expectedActorId, actorReference.ActorId); + Assert.Equal(expectedActorType, actorReference.ActorType); + } + + [Fact] + public void Get_WithInvalidObjectType_ThrowArgumentOutOfRangeException() + { + // Arrange + var actor = new object(); + + // Act + var act = () => ActorReference.Get(actor); + + // Assert + var exception = Assert.Throws(act); + Assert.Equal("actor", exception.ParamName); + Assert.Equal("Invalid actor object type. (Parameter 'actor')", exception.Message); + } + } + + public interface IActorReferenceTestActor : IActor + { + Task GetActorReference(); + } + + public class ActorReferenceTestActor : Actor, IActorReferenceTestActor + { + public ActorReferenceTestActor(ActorHost host) + : base(host) + { + } + + public Task GetActorReference() + { + return Task.FromResult(ActorReference.Get(this)); + } + } +} From ee6ca9e27333ae24420baf7c9c2ce7ed15e28c85 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Wed, 3 Jul 2024 12:47:54 -0500 Subject: [PATCH 20/63] 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 --- src/Dapr.Client/DaprClient.cs | 40 ++++++++++-- src/Dapr.Client/DaprClientGrpc.cs | 46 +++++++++++++- .../{ => Extensions}/EnumExtensions.cs | 5 +- src/Dapr.Client/Extensions/HttpExtensions.cs | 51 +++++++++++++++ .../DaprClientTest.InvokeMethodAsync.cs | 40 ++++++++++++ .../{ => Extensions}/EnumExtensionTest.cs | 6 +- .../Extensions/HttpExtensionTest.cs | 63 +++++++++++++++++++ 7 files changed, 238 insertions(+), 13 deletions(-) rename src/Dapr.Client/{ => Extensions}/EnumExtensions.cs (88%) create mode 100644 src/Dapr.Client/Extensions/HttpExtensions.cs rename test/Dapr.Client.Test/{ => Extensions}/EnumExtensionTest.cs (87%) create mode 100644 test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 21777105b..4f89d8668 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -306,6 +306,20 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName); } + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the POST HTTP method. + /// + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, IReadOnlyCollection> queryStringParameters) + { + return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, queryStringParameters); + } + /// /// Creates an that can be used to perform service invocation for the /// application identified by and invokes the method specified by @@ -317,6 +331,19 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN /// An for use with SendInvokeMethodRequestAsync. public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName); + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, + string methodName, IReadOnlyCollection> queryStringParameters); + /// /// Creates an that can be used to perform service invocation for the /// application identified by and invokes the method specified by @@ -329,9 +356,9 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN /// An for use with SendInvokeMethodRequestAsync. public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, TRequest data) { - return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, data); + return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, new List>(), data); } - + /// /// Creates an that can be used to perform service invocation for the /// application identified by and invokes the method specified by @@ -343,9 +370,10 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri /// The Dapr application id to invoke the method on. /// The name of the method to invoke. /// The data that will be JSON serialized and provided as the request body. + /// A collection of key/value pairs to populate the query string from. /// An for use with SendInvokeMethodRequestAsync. - public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, TRequest data); - + public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, IReadOnlyCollection> queryStringParameters, TRequest data); + /// /// Perform health-check of Dapr sidecar. Return 'true' if sidecar is healthy. Otherwise 'false'. /// CheckHealthAsync handle and will return 'false' if error will occur on transport level @@ -526,7 +554,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, data); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); return InvokeMethodAsync(request, cancellationToken); } @@ -620,7 +648,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, data); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); return InvokeMethodAsync(request, cancellationToken); } diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index 3cd7de526..af245afc3 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -345,7 +345,32 @@ public override async Task InvokeBindingAsync(BindingRequest re #region InvokeMethod Apis + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// An for use with SendInvokeMethodRequestAsync. public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName) + { + return CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>()); + } + + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, + IReadOnlyCollection> queryStringParameters) { ArgumentVerifier.ThrowIfNull(httpMethod, nameof(httpMethod)); ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId)); @@ -356,7 +381,8 @@ public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMeth // // This approach avoids some common pitfalls that could lead to undesired encoding. var path = $"/v1.0/invoke/{appId}/method/{methodName.TrimStart('/')}"; - var request = new HttpRequestMessage(httpMethod, new Uri(this.httpEndpoint, path)); + var requestUri = new Uri(this.httpEndpoint, path).AddQueryParameters(queryStringParameters); + var request = new HttpRequestMessage(httpMethod, requestUri); request.Options.Set(new HttpRequestOptionsKey(AppIdKey), appId); request.Options.Set(new HttpRequestOptionsKey(MethodNameKey), methodName); @@ -369,13 +395,27 @@ public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMeth return request; } - public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, TRequest data) + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by and a JSON serialized request body specified by + /// . + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, + IReadOnlyCollection> queryStringParameters, TRequest data) { ArgumentVerifier.ThrowIfNull(httpMethod, nameof(httpMethod)); ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId)); ArgumentVerifier.ThrowIfNull(methodName, nameof(methodName)); - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, queryStringParameters); request.Content = JsonContent.Create(data, options: this.JsonSerializerOptions); return request; } diff --git a/src/Dapr.Client/EnumExtensions.cs b/src/Dapr.Client/Extensions/EnumExtensions.cs similarity index 88% rename from src/Dapr.Client/EnumExtensions.cs rename to src/Dapr.Client/Extensions/EnumExtensions.cs index 6b058ca77..df9c9ad33 100644 --- a/src/Dapr.Client/EnumExtensions.cs +++ b/src/Dapr.Client/Extensions/EnumExtensions.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable using System; using System.Reflection; using System.Runtime.Serialization; @@ -27,12 +28,14 @@ internal static class EnumExtensions /// public static string GetValueFromEnumMember(this T value) where T : Enum { + ArgumentNullException.ThrowIfNull(value, nameof(value)); + var memberInfo = typeof(T).GetMember(value.ToString(), BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); if (memberInfo.Length <= 0) return value.ToString(); var attributes = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false); - return attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString(); + return (attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString()) ?? value.ToString(); } } } diff --git a/src/Dapr.Client/Extensions/HttpExtensions.cs b/src/Dapr.Client/Extensions/HttpExtensions.cs new file mode 100644 index 000000000..259d2747d --- /dev/null +++ b/src/Dapr.Client/Extensions/HttpExtensions.cs @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------ +// 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. +// ------------------------------------------------------------------------ + +#nullable enable +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapr.Client +{ + /// + /// Provides extensions specific to HTTP types. + /// + internal static class HttpExtensions + { + /// + /// Appends key/value pairs to the query string on an HttpRequestMessage. + /// + /// The uri to append the query string parameters to. + /// The key/value pairs to populate the query string with. + public static Uri AddQueryParameters(this Uri? uri, + IReadOnlyCollection>? queryStringParameters) + { + ArgumentNullException.ThrowIfNull(uri, nameof(uri)); + if (queryStringParameters is null) + return uri; + + var uriBuilder = new UriBuilder(uri); + var qsBuilder = new StringBuilder(uriBuilder.Query); + foreach (var kvParam in queryStringParameters) + { + if (qsBuilder.Length > 0) + qsBuilder.Append('&'); + qsBuilder.Append($"{Uri.EscapeDataString(kvParam.Key)}={Uri.EscapeDataString(kvParam.Value)}"); + } + + uriBuilder.Query = qsBuilder.ToString(); + return uriBuilder.Uri; + } + } +} diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs index 5d46000a1..484f327d0 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs @@ -518,6 +518,18 @@ public async Task CreateInvokeMethodRequest_TransformsUrlCorrectly(string method Assert.Equal(new Uri(expected).AbsoluteUri, request.RequestUri.AbsoluteUri); } + [Fact] + public async Task CreateInvokeMethodRequest_AppendQueryStringValuesCorrectly() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "mymethod", (IReadOnlyCollection>)new List> { new("a", "0"), new("b", "1") }); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); + } + [Fact] public async Task CreateInvokeMethodRequest_WithoutApiToken_CreatesHttpRequestWithoutApiTokenHeader() { @@ -617,6 +629,34 @@ public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContent() Assert.Equal(data.Color, actual.Color); } + [Fact] + public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContentWithQueryString() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget + { + Color = "red", + }; + + var request = client.InnerClient.CreateInvokeMethodRequest(HttpMethod.Post, "test-app", "test", new List> { new("a", "0"), new("b", "1") }, data); + + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/test?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); + + var content = Assert.IsType(request.Content); + Assert.Equal(typeof(Widget), content.ObjectType); + Assert.Same(data, content.Value); + + // the best way to verify the usage of the correct settings object + var actual = await content.ReadFromJsonAsync(this.jsonSerializerOptions); + Assert.Equal(data.Color, actual.Color); + } + + + [Fact] public async Task InvokeMethodWithResponseAsync_ReturnsMessageWithoutCheckingStatus() { diff --git a/test/Dapr.Client.Test/EnumExtensionTest.cs b/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs similarity index 87% rename from test/Dapr.Client.Test/EnumExtensionTest.cs rename to test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs index be78c3861..83c4354f9 100644 --- a/test/Dapr.Client.Test/EnumExtensionTest.cs +++ b/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs @@ -1,7 +1,7 @@ using System.Runtime.Serialization; using Xunit; -namespace Dapr.Client.Test +namespace Dapr.Client.Test.Extensions { public class EnumExtensionTest { @@ -29,9 +29,9 @@ public void GetValueFromEnumMember_BlueResolvesAsExpected() public enum TestEnum { - [EnumMember(Value="red")] + [EnumMember(Value = "red")] Red, - [EnumMember(Value="YELLOW")] + [EnumMember(Value = "YELLOW")] Yellow, Blue } diff --git a/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs b/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs new file mode 100644 index 000000000..7b93c1c91 --- /dev/null +++ b/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Net.Http; +using Xunit; + +namespace Dapr.Client.Test.Extensions +{ + public class HttpExtensionTest + { + [Fact] + public void AddQueryParameters_ReturnsEmptyQueryStringWithNullParameters() + { + const string uri = "https://localhost/mypath"; + var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); + var updatedUri = httpRq.RequestUri.AddQueryParameters(null); + Assert.Equal(uri, updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_ReturnsOriginalQueryStringWithNullParameters() + { + const string uri = "https://localhost/mypath?a=0&b=1"; + var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); + var updatedUri = httpRq.RequestUri.AddQueryParameters(null); + Assert.Equal(uri, updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_BuildsQueryString() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath?a=0"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> + { + new("test", "value") + }); + Assert.Equal("https://localhost/mypath?a=0&test=value", updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_BuildQueryStringWithDuplicateKeys() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> + { + new("test", "1"), + new("test", "2"), + new("test", "3") + }); + Assert.Equal("https://localhost/mypath?test=1&test=2&test=3", updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_EscapeSpacesInValues() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> + { + new("name1", "John Doe"), + new("name2", "Jane Doe") + }); + Assert.Equal("https://localhost/mypath?name1=John%20Doe&name2=Jane%20Doe", updatedUri.AbsoluteUri); + } + } +} From cd1351a864ebe2bd523a2086282b1d508cd5907f Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 10 Jul 2024 22:41:45 +0200 Subject: [PATCH 21/63] Fixed actorProxy argument null check Signed-off-by: Manuel Menegazzo --- .../Helpers/SyntaxFactoryHelpers.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index ce4e446a2..0ccdd8162 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -17,12 +17,14 @@ public static ThrowExpressionSyntax ArgumentNullExceptionSyntax(string argumentN { return SyntaxFactory.ThrowExpression( SyntaxFactory.Token(SyntaxKind.ThrowKeyword), - SyntaxFactory.InvocationExpression( + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.Token(SyntaxKind.NewKeyword), SyntaxFactory.ParseTypeName("System.ArgumentNullException"), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { - SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName)) - })) + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(argumentName))) + })), + default ) ); } From ef9d0c29fc9287c91e517bdf657134f451545130 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 10 Jul 2024 22:46:36 +0200 Subject: [PATCH 22/63] Moved ActorClientDesciptor into separta cs file Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 42 +---------------- .../Models/ActorClientDescriptor.cs | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index ccee0e089..633ea5f8a 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -16,6 +16,7 @@ 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; @@ -256,47 +257,6 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient } } - /// - /// Describes an actor client to generate. - /// - private record class ActorClientDescriptor : IEquatable - { - /// - /// Gets the actor interface symbol. - /// - 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 IEnumerable Methods { get; set; } = Array.Empty(); - - /// - /// Compilation to use for generating the client. - /// - public Compilation Compilation { get; set; } = null!; - } - /// /// Returns the syntax kinds for the specified accessibility. /// diff --git a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs new file mode 100644 index 000000000..652e5d8db --- /dev/null +++ b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs @@ -0,0 +1,45 @@ +using Microsoft.CodeAnalysis; + +namespace Dapr.Actors.Generators.Models +{ + /// + /// Describes an actor client to generate. + /// + internal record class ActorClientDescriptor : IEquatable + { + /// + /// Gets the actor interface symbol. + /// + 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 IEnumerable Methods { get; set; } = Array.Empty(); + + /// + /// Compilation to use for generating the client. + /// + public Compilation Compilation { get; set; } = null!; + } +} From 25f11e23c08a6e7d9feb3aef36c7c815ae5df25c Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 11 Jul 2024 09:04:48 +0200 Subject: [PATCH 23/63] Moved textual templates to dedicated class Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 80 ++--------------- .../{GeneratorConstants.cs => Constants.cs} | 2 +- src/Dapr.Actors.Generators/Templates.cs | 87 +++++++++++++++++++ 3 files changed, 96 insertions(+), 73 deletions(-) rename src/Dapr.Actors.Generators/{GeneratorConstants.cs => Constants.cs} (96%) create mode 100644 src/Dapr.Actors.Generators/Templates.cs diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 633ea5f8a..11a837b47 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -12,7 +12,6 @@ // ------------------------------------------------------------------------ using System.Collections.Immutable; -using System.Text; using Dapr.Actors.Generators.Diagnostics; using Dapr.Actors.Generators.Extensions; using Dapr.Actors.Generators.Helpers; @@ -20,7 +19,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; namespace Dapr.Actors.Generators; @@ -30,68 +28,6 @@ namespace Dapr.Actors.Generators; [Generator] public sealed class ActorClientGenerator : IIncrementalGenerator { - private static SourceText ActorMethodAttributeSourceText(string generatorNamespace) - { - if (generatorNamespace == null) - { - throw new ArgumentNullException(nameof(generatorNamespace)); - } - - var source = $@" -// - -#nullable enable - -using System; - -namespace {generatorNamespace} -{{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - internal sealed class {GeneratorConstants.ActorMethodAttributeTypeName} : Attribute - {{ - public string? Name {{ get; set; }} - }} -}}"; - - return SourceText.From( - SyntaxFactory.ParseCompilationUnit(source) - .NormalizeWhitespace() - .ToFullString(), - Encoding.UTF8); - } - - private static SourceText GenerateActorClientAttributeSourceText(string generatorNamespace) - { - if (generatorNamespace == null) - { - throw new ArgumentNullException(nameof(generatorNamespace)); - } - - string source = $@" -// - -#nullable enable - -using System; - -namespace {generatorNamespace} -{{ - [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - internal sealed class {GeneratorConstants.GenerateActorClientAttributeTypeName} : Attribute - {{ - public string? Name {{ get; set; }} - - public string? Namespace {{ get; set; }} - }} -}}"; - - return SourceText.From( - SyntaxFactory.ParseCompilationUnit(source) - .NormalizeWhitespace() - .ToFullString(), - Encoding.UTF8); - } - /// public void Initialize(IncrementalGeneratorInitializationContext context) { @@ -99,18 +35,18 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterPostInitializationOutput(context => { context.AddSource( - $"{GeneratorConstants.ActorMethodAttributeFullTypeName}.g.cs", - ActorMethodAttributeSourceText(GeneratorConstants.GeneratorsNamespace)); + $"{Constants.ActorMethodAttributeFullTypeName}.g.cs", + Templates.ActorMethodAttributeSourceText(Constants.GeneratorsNamespace)); context.AddSource( - $"{GeneratorConstants.GenerateActorClientAttributeFullTypeName}.g.cs", - GenerateActorClientAttributeSourceText(GeneratorConstants.GeneratorsNamespace)); + $"{Constants.GenerateActorClientAttributeFullTypeName}.g.cs", + Templates.GenerateActorClientAttributeSourceText(Constants.GeneratorsNamespace)); }); // Register the value provider that triggers the generation of actor clients based on the GenerateActorClientAttribute. IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider .ForAttributeWithMetadataName( - GeneratorConstants.GenerateActorClientAttributeFullTypeName, + Constants.GenerateActorClientAttributeFullTypeName, predicate: static (_, _) => true, transform: static (gasc, cancellationToken) => GetActorClientDescription(gasc, cancellationToken)); @@ -169,11 +105,11 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient try { - var actorMethodAttributeSymbol = descriptor.Compilation.GetTypeByMetadataName(GeneratorConstants.ActorMethodAttributeFullTypeName) - ?? throw new InvalidOperationException("Could not find ActorMethodAttribute."); + var actorMethodAttributeSymbol = descriptor.Compilation.GetTypeByMetadataName(Constants.ActorMethodAttributeFullTypeName) + ?? throw new InvalidOperationException("Could not find ActorMethodAttribute type."); var cancellationTokenSymbol = descriptor.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken") - ?? throw new InvalidOperationException("Could not find CancellationToken."); + ?? throw new InvalidOperationException("Could not find CancellationToken type."); var actorClientBaseInterface = SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(descriptor.InterfaceType.ToString())); var autoGeneratedComment = SyntaxFactory.Comment("// "); diff --git a/src/Dapr.Actors.Generators/GeneratorConstants.cs b/src/Dapr.Actors.Generators/Constants.cs similarity index 96% rename from src/Dapr.Actors.Generators/GeneratorConstants.cs rename to src/Dapr.Actors.Generators/Constants.cs index aa57cf120..c00f04bb6 100644 --- a/src/Dapr.Actors.Generators/GeneratorConstants.cs +++ b/src/Dapr.Actors.Generators/Constants.cs @@ -3,7 +3,7 @@ /// /// Constants used by the code generator. /// - internal static class GeneratorConstants + internal static class Constants { /// /// The namespace used by the generated code. diff --git a/src/Dapr.Actors.Generators/Templates.cs b/src/Dapr.Actors.Generators/Templates.cs new file mode 100644 index 000000000..2ed085b30 --- /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 source text for the ActorMethodAttribute. + /// + /// + /// + /// + public static SourceText ActorMethodAttributeSourceText(string generatorNamespace) + { + if (generatorNamespace == null) + { + throw new ArgumentNullException(nameof(generatorNamespace)); + } + + var source = $@" +// + +#nullable enable + +using System; + +namespace {generatorNamespace} +{{ + [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 source text for the GenerateActorClientAttribute. + /// + /// + /// + /// + public static SourceText GenerateActorClientAttributeSourceText(string generatorNamespace) + { + if (generatorNamespace == null) + { + throw new ArgumentNullException(nameof(generatorNamespace)); + } + + string source = $@" +// + +#nullable enable + +using System; + +namespace {generatorNamespace} +{{ + [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); + } + } +} From 182ce2df7341898c0687dae40a290c6bed5bdd75 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 11 Jul 2024 09:22:18 +0200 Subject: [PATCH 24/63] Updated comments, property names Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 18 +++++++++++------ .../Models/ActorClientDescriptor.cs | 5 +++-- src/Dapr.Actors.Generators/Templates.cs | 20 +++++++++---------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 11a837b47..3edd5a9a3 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -43,12 +43,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context) Templates.GenerateActorClientAttributeSourceText(Constants.GeneratorsNamespace)); }); - // Register the value provider that triggers the generation of actor clients based on the GenerateActorClientAttribute. + // 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) => GetActorClientDescription(gasc, cancellationToken)); + transform: static (gasc, cancellationToken) => CreateActorClientDescriptor(gasc, cancellationToken)); // Register the source output that generates the actor clients. context.RegisterSourceOutput(actorClientsToGenerate, GenerateActorClientCode); @@ -61,7 +61,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) /// /// /// - static ActorClientDescriptor? GetActorClientDescription( + static ActorClientDescriptor? CreateActorClientDescriptor( GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { @@ -71,11 +71,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var actorInterfaceSymbol = (INamedTypeSymbol)context.TargetSymbol; - // Use the namespace specified in the attribute, or the namespace of the actor interface if not specified. + // 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(); - // Use the name specified in the attribute, or the name of the actor interface with a "Client" suffix if not specified. + // 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"; @@ -83,7 +83,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .GetMembers() .OfType() .Where(m => m.MethodKind == MethodKind.Ordinary) - .ToList(); + .ToImmutableArray(); return new ActorClientDescriptor { @@ -96,6 +96,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context) }; } + /// + /// Generates the actor client code based on the specified descriptor. + /// + /// + /// + /// static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor? descriptor) { if (descriptor is null) diff --git a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs index 652e5d8db..b87be6631 100644 --- a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs +++ b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs @@ -1,4 +1,5 @@ -using Microsoft.CodeAnalysis; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; namespace Dapr.Actors.Generators.Models { @@ -35,7 +36,7 @@ internal record class ActorClientDescriptor : IEquatable /// /// Methods to generate in the client. /// - public IEnumerable Methods { get; set; } = Array.Empty(); + public ImmutableArray Methods { get; set; } = Array.Empty().ToImmutableArray(); /// /// Compilation to use for generating the client. diff --git a/src/Dapr.Actors.Generators/Templates.cs b/src/Dapr.Actors.Generators/Templates.cs index 2ed085b30..fe393ddb9 100644 --- a/src/Dapr.Actors.Generators/Templates.cs +++ b/src/Dapr.Actors.Generators/Templates.cs @@ -13,14 +13,14 @@ internal static partial class Templates /// /// Returns the source text for the ActorMethodAttribute. /// - /// + /// /// /// - public static SourceText ActorMethodAttributeSourceText(string generatorNamespace) + public static SourceText ActorMethodAttributeSourceText(string destinationNamespace) { - if (generatorNamespace == null) + if (destinationNamespace == null) { - throw new ArgumentNullException(nameof(generatorNamespace)); + throw new ArgumentNullException(nameof(destinationNamespace)); } var source = $@" @@ -30,7 +30,7 @@ public static SourceText ActorMethodAttributeSourceText(string generatorNamespac using System; -namespace {generatorNamespace} +namespace {destinationNamespace} {{ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] internal sealed class {Constants.ActorMethodAttributeTypeName} : Attribute @@ -49,14 +49,14 @@ internal sealed class {Constants.ActorMethodAttributeTypeName} : Attribute /// /// Returns the source text for the GenerateActorClientAttribute. /// - /// + /// /// /// - public static SourceText GenerateActorClientAttributeSourceText(string generatorNamespace) + public static SourceText GenerateActorClientAttributeSourceText(string destinationNamespace) { - if (generatorNamespace == null) + if (destinationNamespace == null) { - throw new ArgumentNullException(nameof(generatorNamespace)); + throw new ArgumentNullException(nameof(destinationNamespace)); } string source = $@" @@ -66,7 +66,7 @@ public static SourceText GenerateActorClientAttributeSourceText(string generator using System; -namespace {generatorNamespace} +namespace {destinationNamespace} {{ [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] internal sealed class {Constants.GenerateActorClientAttributeTypeName} : Attribute From f8d83cbf2d1e3b05340af3d59cde7a06a712ed72 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 11 Jul 2024 10:32:39 +0200 Subject: [PATCH 25/63] Added argument null check to SyntaxFactoryHelpers Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 9 +-------- .../Helpers/SyntaxFactoryHelpers.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 3edd5a9a3..fdedbe5bb 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -142,14 +142,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient }))) .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { - SyntaxFactory.IfStatement( - SyntaxFactory.BinaryExpression( - SyntaxKind.IsExpression, - SyntaxFactory.IdentifierName("actorProxy"), - SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression) - ), - SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ArgumentNullExceptionSyntax("actorProxy")) - ), + SyntaxFactoryHelpers.ThrowIfArgumentNull("actorProxy"), SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName("this.actorProxy"), diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 0ccdd8162..cf86f2652 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -28,5 +28,25 @@ public static ThrowExpressionSyntax ArgumentNullExceptionSyntax(string argumentN ) ); } + + /// + /// Generates a syntax for null check for the given argument name. + /// + /// + /// + 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(ArgumentNullExceptionSyntax(argumentName)) + })) + ); + } } } From afaac59b5911687a804ee20120bb8709579a8c52 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 11 Jul 2024 11:12:58 +0200 Subject: [PATCH 26/63] Added comments Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 19 +- .../ActorClientGeneratorOld.cs | 273 ------------------ 2 files changed, 14 insertions(+), 278 deletions(-) delete mode 100644 src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index fdedbe5bb..7ef0f3a23 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -79,6 +79,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var clientName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Name").Value.Value?.ToString() ?? $"{(actorInterfaceSymbol.Name.StartsWith("I") ? actorInterfaceSymbol.Name.Substring(1) : actorInterfaceSymbol.Name)}Client"; + // Actor member to generate the client for. var members = actorInterfaceSymbol .GetMembers() .OfType() @@ -121,11 +122,6 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var autoGeneratedComment = SyntaxFactory.Comment("// "); var nullableAnnotation = SyntaxFactory.Trivia(SyntaxFactory.NullableDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.EnableKeyword), true)); - var actorClientClassModifiers = new List() - .Concat(GetSyntaxKinds(descriptor.Accessibility)) - .Concat(SyntaxKind.SealedKeyword) - .Select(sk => SyntaxFactory.Token(sk)); - var actorProxyField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) .WithModifiers(SyntaxFactory.TokenList(new[] @@ -162,6 +158,11 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient .Concat(actorMethods) .ToList(); + var actorClientClassModifiers = new List() + .Concat(GetSyntaxKinds(descriptor.Accessibility)) + .Concat(SyntaxKind.SealedKeyword) + .Select(sk => SyntaxFactory.Token(sk)); + var actorClientClassDeclaration = SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName) .WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers)) .WithMembers(SyntaxFactory.List(actorMembers)) @@ -227,6 +228,14 @@ private static IEnumerable GetSyntaxKinds(Accessibility accessibilit return syntaxKinds; } + /// + /// Generates the method implementation for the specified method. + /// + /// + /// + /// + /// + /// private static string GenerateMethodImplementation( SourceProductionContext context, IMethodSymbol method, diff --git a/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs b/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs deleted file mode 100644 index b1a38a8cd..000000000 --- a/src/Dapr.Actors.Generators/ActorClientGeneratorOld.cs +++ /dev/null @@ -1,273 +0,0 @@ -// ------------------------------------------------------------------------ -// 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. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ------------------------------------------------------------------------ - -using Dapr.Actors.Generators.Extensions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Dapr.Actors.Generators; - -/// -/// Generates strongly-typed actor clients that use the non-remoting actor proxy. -/// -//[Generator] -public sealed class ActorClientGeneratorOld : ISourceGenerator -{ - 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 - { - private readonly List models = new(); - - public IEnumerable Models => this.models; - - #region ISyntaxContextReceiver Members - - public void OnVisitSyntaxNode(GeneratorSyntaxContext 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 - } - - #region ISourceGenerator Members - - /// - public void Execute(GeneratorExecutionContext context) - { - 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 actorInterfaceTypeName = interfaceSymbol.Name; - 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(); - - var methodImplementations = String.Join("\n", members.Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol))); - - var source = $@" -// - -namespace {namespaceName} -{{ - {accessibility} sealed class {clientTypeName} : {fullyQualifiedActorInterfaceTypeName} - {{ - private readonly Dapr.Actors.Client.ActorProxy actorProxy; - - public {clientTypeName}(Dapr.Actors.Client.ActorProxy actorProxy) - {{ - this.actorProxy = actorProxy; - }} - - {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); - } - } - } - } - - /// - public void Initialize(GeneratorInitializationContext context) - { - /* - while (!Debugger.IsAttached) - { - System.Threading.Thread.Sleep(500); - } - */ - - context.RegisterForPostInitialization( - i => - { - i.AddSource($"{ActorMethodAttributeFullTypeName}.g.cs", ActorMethodAttributeText); - i.AddSource($"{GenerateActorClientAttributeFullTypeName}.g.cs", GenerateActorClientAttributeText); - }); - - context.RegisterForSyntaxNotifications(() => new ActorInterfaceSyntaxReceiver()); - } - - #endregion - - private static string GetClientAccessibility(INamedTypeSymbol interfaceSymbol) - { - return interfaceSymbol.DeclaredAccessibility switch - { - 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; - } - - private static string 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; - - 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()) - }); - } - - 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()) - }); - } - - 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) - { - argumentDefinitions = argumentDefinitions + " = default"; - } - - string argumentList = String.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); - - string templateArgs = - returnTypeArgument is not null - ? $"<{(requestParameter is not null ? $"{requestParameter.Type}, " : "")}{returnTypeArgument}>" - : ""; - - return - $@"public {method.ReturnType} {method.Name}({argumentDefinitions}) - {{ - return this.actorProxy.InvokeMethodAsync{templateArgs}({argumentList}); - }}"; - } -} From 92c0a9ec0054f4e2a5baa9ddc78513d5f91bb222 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 11 Jul 2024 11:14:59 +0200 Subject: [PATCH 27/63] 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 --- .../ActorClientGeneratorTests.cs | 6 +++--- .../CSharpSourceGeneratorVerifier.cs | 5 ++--- .../Dapr.Actors.Generators.Test.csproj | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs index d4d8cc418..908ef814e 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. @@ -17,7 +17,7 @@ namespace Dapr.Actors.Generators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; -using VerifyCS = CSharpSourceGeneratorVerifier; +using VerifyCS = CSharpSourceGeneratorVerifier; public sealed class ActorClientGeneratorTests { @@ -693,4 +693,4 @@ public interface ITestActor 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 27d8bc5e0..c64fd3427 100644 --- a/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs +++ b/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs @@ -16,15 +16,14 @@ 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() { - public class Test : CSharpSourceGeneratorTest + public class Test : CSharpSourceGeneratorTest { public Test() { 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 93f9c6d7c..5f2902fc7 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -13,7 +13,7 @@ - + From d75c83bddbd72b3fd67d81432eb2b197fdfc2cb5 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 11 Jul 2024 12:01:08 +0200 Subject: [PATCH 28/63] Adapted existing unit test to new source generated code Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 14 +- .../ActorClientGeneratorTests.cs | 282 +++++++++--------- 2 files changed, 156 insertions(+), 140 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 7ef0f3a23..0a22451dc 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -149,7 +149,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var actorMethods = descriptor.Methods .OrderBy(member => member.DeclaredAccessibility) .ThenBy(member => member.Name) - .Select(member => GenerateMethodImplementation(context, member, actorMethodAttributeSymbol, cancellationTokenSymbol)) + .Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol)) .Select(m => SyntaxFactory.ParseMemberDeclaration(m)!); var actorMembers = new List() @@ -231,28 +231,32 @@ private static IEnumerable GetSyntaxKinds(Accessibility accessibilit /// /// Generates the method implementation for the specified method. /// - /// /// /// /// /// private static string GenerateMethodImplementation( - SourceProductionContext context, 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) { - context.ReportDiagnostic(CancellationTokensMustBeTheLastArgument.CreateDiagnostic(cancellationTokenParameter)); + diagnostics.Add(CancellationTokensMustBeTheLastArgument.CreateDiagnostic(cancellationTokenParameter)); } if ((method.Parameters.Length > 1 && cancellationTokenIndex == -1) || (method.Parameters.Length > 2)) { - context.ReportDiagnostic(MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.CreateDiagnostic(method)); + diagnostics.Add(MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.CreateDiagnostic(method)); + } + + if (diagnostics.Any()) + { + throw new DiagnosticsException(diagnostics); } var attributeData = method.GetAttributes() diff --git a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs index 908ef814e..9fd11eb70 100644 --- a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs +++ b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs @@ -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(""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(""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(""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(""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(""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,17 +318,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(""actorProxy""); + } + this.actorProxy = actorProxy; } @@ -332,8 +340,7 @@ public System.Threading.Tasks.Task TestMethod(Test.TestValue 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(""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(""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(""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(""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,17 +545,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(""actorProxy""); + } + this.actorProxy = actorProxy; } @@ -553,8 +567,7 @@ public System.Threading.Tasks.Task TestMethodAsync(Test.TestValue value, System. return this.actorProxy.InvokeMethodAsync(""TestMethodAsync"", value, cancellationToken); } } -} -"; +}"; await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync(); } @@ -579,17 +592,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(""actorProxy""); + } + this.actorProxy = actorProxy; } @@ -598,8 +614,7 @@ public System.Threading.Tasks.Task TestMethodAsync(Test.TestValue value, System. 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,15 +694,14 @@ 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(); } From 22ac5653aa901e259ca8616c53db8fe3f5b87e2f Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 11 Jul 2024 16:02:15 +0200 Subject: [PATCH 29/63] Up Signed-off-by: Manuel Menegazzo --- .../Helpers/SyntaxFactoryHelpers.cs | 4 +-- .../Helpers/SyntaxFactoryHelpersTests.cs | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index cf86f2652..fa42d4b77 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -13,7 +13,7 @@ public static partial class SyntaxFactoryHelpers /// /// /// - public static ThrowExpressionSyntax ArgumentNullExceptionSyntax(string argumentName) + public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argumentName) { return SyntaxFactory.ThrowExpression( SyntaxFactory.Token(SyntaxKind.ThrowKeyword), @@ -44,7 +44,7 @@ public static IfStatementSyntax ThrowIfArgumentNull(string argumentName) ), SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { - SyntaxFactory.ExpressionStatement(ArgumentNullExceptionSyntax(argumentName)) + SyntaxFactory.ExpressionStatement(ThrowArgumentNullExceptionSyntax(argumentName)) })) ); } 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..41e07911c --- /dev/null +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -0,0 +1,27 @@ +using Dapr.Actors.Generators.Helpers; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Dapr.Actors.Generators.Test.Helpers +{ + public class SyntaxFactoryHelpersTests + { + [Fact] + public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionSyntaxWithGivenArgumentName() + { + // Arrange + var argumentName = "arg0"; + var expectedSource = "throw new System.ArgumentNullException(\"arg0\");"; + + // Act + var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullExceptionSyntax(argumentName)) + .SyntaxTree + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Assert + Assert.Equal(expectedSource, generatedSource); + } + } +} From 3c52343f649a4290087146012ae708b598caad25 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Fri, 12 Jul 2024 09:10:25 +0200 Subject: [PATCH 30/63] Added tests for SyntaxFactoryHelpers Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 2 +- .../Helpers/SyntaxFactoryHelpers.cs | 2 +- .../Helpers/SyntaxFactoryHelpersTests.cs | 23 ++++++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 0a22451dc..d7bb49ebb 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -138,7 +138,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient }))) .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { - SyntaxFactoryHelpers.ThrowIfArgumentNull("actorProxy"), + SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax("actorProxy"), SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName("this.actorProxy"), diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index fa42d4b77..c32ca302f 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -34,7 +34,7 @@ public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argu /// /// /// - public static IfStatementSyntax ThrowIfArgumentNull(string argumentName) + public static IfStatementSyntax ThrowIfArgumentNullSyntax(string argumentName) { return SyntaxFactory.IfStatement( SyntaxFactory.BinaryExpression( diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index 41e07911c..5fe0f2b7c 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -11,7 +11,7 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS { // Arrange var argumentName = "arg0"; - var expectedSource = "throw new System.ArgumentNullException(\"arg0\");"; + var expectedSource = $@"throw new System.ArgumentNullException(""arg0"");"; // Act var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullExceptionSyntax(argumentName)) @@ -23,5 +23,26 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS // Assert Assert.Equal(expectedSource, generatedSource); } + + [Fact] + public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgumentName() + { + // Arrange + var argumentName = "arg0"; + var expectedSource = $@"if (arg0 is null) +{{ + throw new System.ArgumentNullException(""arg0""); +}}"; + + // Act + var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax(argumentName) + .SyntaxTree + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + // Assert + Assert.Equal(expectedSource, generatedSource); + } } } From 3208ec666172b213d4c1837c2e1bd02cb367c6cf Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Fri, 12 Jul 2024 10:21:17 +0200 Subject: [PATCH 31/63] Updated generation of ArgumentNullException Signed-off-by: Manuel Menegazzo --- .../Helpers/SyntaxFactoryHelpers.cs | 10 +++++++- .../ActorClientGeneratorTests.cs | 24 +++++++++---------- .../Helpers/SyntaxFactoryHelpersTests.cs | 4 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index c32ca302f..192c6a719 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -22,7 +22,15 @@ public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argu SyntaxFactory.ParseTypeName("System.ArgumentNullException"), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { - SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(argumentName))) + SyntaxFactory.Argument( + SyntaxFactory.InvocationExpression( + SyntaxFactory.IdentifierName("nameof"), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] + { + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName)) + })) + ) + ) })), default ) diff --git a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs index 9fd11eb70..24e8dba23 100644 --- a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs +++ b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs @@ -109,7 +109,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -152,7 +152,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -195,7 +195,7 @@ public MyTestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -238,7 +238,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -283,7 +283,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -329,7 +329,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -374,7 +374,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -421,7 +421,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -465,7 +465,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -509,7 +509,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -556,7 +556,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; @@ -603,7 +603,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) { if (actorProxy is null) { - throw new System.ArgumentNullException(""actorProxy""); + throw new System.ArgumentNullException(nameof(actorProxy)); } this.actorProxy = actorProxy; diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index 5fe0f2b7c..295eac78b 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -11,7 +11,7 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS { // Arrange var argumentName = "arg0"; - var expectedSource = $@"throw new System.ArgumentNullException(""arg0"");"; + var expectedSource = $@"throw new System.ArgumentNullException(nameof(arg0));"; // Act var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullExceptionSyntax(argumentName)) @@ -31,7 +31,7 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu var argumentName = "arg0"; var expectedSource = $@"if (arg0 is null) {{ - throw new System.ArgumentNullException(""arg0""); + throw new System.ArgumentNullException(nameof(arg0)); }}"; // Act From 495cff6b1f8d2118c7ba015bb3ec9c56653760cb Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 15 Jul 2024 13:42:31 +0200 Subject: [PATCH 32/63] Updated nullability Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index d7bb49ebb..34e703ca2 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -44,7 +44,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) }); // Register the value provider that triggers the generation of actor clients when detecting the GenerateActorClientAttribute. - IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider + IncrementalValuesProvider actorClientsToGenerate = context.SyntaxProvider .ForAttributeWithMetadataName( Constants.GenerateActorClientAttributeFullTypeName, predicate: static (_, _) => true, @@ -61,7 +61,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) /// /// /// - static ActorClientDescriptor? CreateActorClientDescriptor( + static ActorClientDescriptor CreateActorClientDescriptor( GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { @@ -103,13 +103,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) /// /// /// - static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor? descriptor) + static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor descriptor) { - if (descriptor is null) - { - return; - } - try { var actorMethodAttributeSymbol = descriptor.Compilation.GetTypeByMetadataName(Constants.ActorMethodAttributeFullTypeName) @@ -254,6 +249,7 @@ private static string GenerateMethodImplementation( diagnostics.Add(MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.CreateDiagnostic(method)); } + // If there are any diagnostics, throw an exception to report them. if (diagnostics.Any()) { throw new DiagnosticsException(diagnostics); From 403e95ffc607a6b41f77aaa33e69867684d63725 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 25 Jul 2024 22:06:54 +0200 Subject: [PATCH 33/63] Fixed internal methods tests Signed-off-by: Manuel Menegazzo --- .../Dapr.Actors.Generators.csproj | 4 ++++ .../Extensions/IEnumerableExtensions.cs | 4 ++-- .../Extensions/IEnumerableExtensionsTests.cs | 21 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs diff --git a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj index d898b8c5b..d87d868dd 100644 --- a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj +++ b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj @@ -48,4 +48,8 @@ + + + + diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index 147f326d2..844a088fc 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -9,7 +9,7 @@ internal static class IEnumerableExtensions /// /// /// Return the zero-based index of the first occurrence of an element that satisfies the condition, if found; otherwise, -1. - public static int IndexOf(this IEnumerable source, Func predicate) + internal static int IndexOf(this IEnumerable source, Func predicate) { if (predicate is null) { @@ -38,7 +38,7 @@ public static int IndexOf(this IEnumerable source, Func predicate /// /// /// - public static IEnumerable Concat(this IEnumerable source, T item) + internal static IEnumerable Concat(this IEnumerable source, T item) { return source.Concat(new[] { item }); } 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..1e181ba52 --- /dev/null +++ b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs @@ -0,0 +1,21 @@ +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); + } + } +} From 179ae8b196bcceb1c7f66802edf55f385e50f66a Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 25 Jul 2024 22:10:34 +0200 Subject: [PATCH 34/63] Added test to IEnumerableExtensions Signed-off-by: Manuel Menegazzo --- .../Extensions/IEnumerableExtensionsTests.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs index 1e181ba52..d753eb000 100644 --- a/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs +++ b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs @@ -17,5 +17,50 @@ public void IndexOf_WhenPredicateIsNull_ThrowsArgumentNullException() // 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); + } + + [Fact] + public void Concat_WhenItemIsNotNull_ReturnsConcatenatedSequence() + { + // Arrange + var source = new[] { "a", "b", "c" }; + string item = "d"; + + // Act + var result = source.Concat(item); + + // Assert + Assert.Equal(new[] { "a", "b", "c", "d" }, result); + } } } From b04e5e0d9c1a67401210671c49854672654ee501 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 25 Jul 2024 22:24:00 +0200 Subject: [PATCH 35/63] Unittested GetSyntaxKinds from Accessibility Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 37 +----------------- .../Helpers/SyntaxFactoryHelpers.cs | 38 ++++++++++++++++++- .../Helpers/SyntaxFactoryHelpersTests.cs | 20 ++++++++++ 3 files changed, 58 insertions(+), 37 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 34e703ca2..39047af41 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -154,7 +154,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient .ToList(); var actorClientClassModifiers = new List() - .Concat(GetSyntaxKinds(descriptor.Accessibility)) + .Concat(SyntaxFactoryHelpers.GetSyntaxKinds(descriptor.Accessibility)) .Concat(SyntaxKind.SealedKeyword) .Select(sk => SyntaxFactory.Token(sk)); @@ -188,41 +188,6 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient } } - /// - /// Returns the syntax kinds for the specified accessibility. - /// - /// - /// - /// - private static IEnumerable 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; - } - /// /// Generates the method implementation for the specified method. /// diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 192c6a719..08b358e63 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -1,4 +1,5 @@ -using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Dapr.Actors.Generators.Helpers @@ -56,5 +57,40 @@ public static IfStatementSyntax ThrowIfArgumentNullSyntax(string argumentName) })) ); } + + /// + /// Returns the syntax kinds for the specified accessibility. + /// + /// + /// + /// + 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/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index 295eac78b..8dfde33cd 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -44,5 +44,25 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu // Assert Assert.Equal(expectedSource, 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, SyntaxKind[] expectedSyntaxKinds) + { + // Arrange + + // Act + var generatedSyntaxKinds = SyntaxFactoryHelpers.GetSyntaxKinds(accessibility); + + // Assert + foreach (var expectedSyntaxKind in expectedSyntaxKinds) + { + Assert.Contains(expectedSyntaxKind, generatedSyntaxKinds); + } + } } } From d84981d3a0cba1618fb4cbcb54e31a1ae91a46be Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 25 Jul 2024 23:34:05 +0200 Subject: [PATCH 36/63] UP Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 76 ++++++++++++++----- .../Helpers/SyntaxFactoryHelpers.cs | 10 +-- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 39047af41..6ae1a79d4 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -144,8 +144,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var actorMethods = descriptor.Methods .OrderBy(member => member.DeclaredAccessibility) .ThenBy(member => member.Name) - .Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol)) - .Select(m => SyntaxFactory.ParseMemberDeclaration(m)!); + .Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol)); var actorMembers = new List() .Concat(actorProxyField) @@ -195,7 +194,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient /// /// /// - private static string GenerateMethodImplementation( + private static MethodDeclarationSyntax GenerateMethodImplementation( IMethodSymbol method, INamedTypeSymbol generateActorClientAttributeSymbol, INamedTypeSymbol cancellationTokenSymbol) @@ -223,33 +222,68 @@ private static string GenerateMethodImplementation( 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 daprMethodName = 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 methodModifiers = new List() + .Concat(SyntaxFactoryHelpers.GetSyntaxKinds(method.DeclaredAccessibility)) + .Select(sk => SyntaxFactory.Token(sk)); - var returnTypeArgument = (method.ReturnType as INamedTypeSymbol)?.TypeArguments.FirstOrDefault(); - - string argumentDefinitions = string.Join(", ", method.Parameters.Select(p => $"{p.Type} {p.Name}")); + 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()))); + // Append the CancellationToken parameter if it exists. if (cancellationTokenParameter is not null && cancellationTokenParameter.IsOptional && cancellationTokenParameter.HasExplicitDefaultValue && cancellationTokenParameter.ExplicitDefaultValue is null) { - argumentDefinitions = argumentDefinitions + " = default"; + methodParameters = methodParameters.Append( + SyntaxFactory.Parameter(SyntaxFactory.Identifier(cancellationTokenParameter.Name)) + .WithDefault(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression))) + .WithType(SyntaxFactory.ParseTypeName(cancellationTokenParameter.Type.ToString()))); } - string argumentList = string.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); - - string templateArgs = - returnTypeArgument is not null - ? $"<{(requestParameter is not null ? $"{requestParameter.Type}, " : "")}{returnTypeArgument}>" - : ""; - - return - $@"public {method.ReturnType} {method.Name}({argumentDefinitions}) - {{ - return this.actorProxy.InvokeMethodAsync{templateArgs}({argumentList}); - }}"; + var methodxx = 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[] + { + //SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax("actorProxy"), + //SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( + // SyntaxKind.SimpleAssignmentExpression, + // SyntaxFactory.IdentifierName("this.actorProxy"), + // SyntaxFactory.IdentifierName("actorProxy")) + //), + }))); + + return methodxx; + + //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) + //{ + // argumentDefinitions = argumentDefinitions + " = default"; + //} + + //string argumentList = string.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); + + //string templateArgs = + // returnTypeArgument is not null + // ? $"<{(requestParameter is not null ? $"{requestParameter.Type}, " : "")}{returnTypeArgument}>" + // : ""; + + //return + //$@"public {method.ReturnType} {method.Name}({argumentDefinitions}) + //{{ + // return this.actorProxy.InvokeMethodAsync{templateArgs}({argumentList}); + //}}"; } } diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 08b358e63..588e90447 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -23,15 +23,7 @@ public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argu SyntaxFactory.ParseTypeName("System.ArgumentNullException"), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { - SyntaxFactory.Argument( - SyntaxFactory.InvocationExpression( - SyntaxFactory.IdentifierName("nameof"), - SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] - { - SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argumentName)) - })) - ) - ) + SyntaxFactory.Argument(SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(argumentName))) })), default ) From 4308e11fd1ab1b2bc1b015eb9e1c18f2fe4283dc Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Tue, 30 Jul 2024 14:21:36 +0200 Subject: [PATCH 37/63] Updated assignment implementation of ctor body Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 6ae1a79d4..e1a9e3c0d 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -136,7 +136,10 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax("actorProxy"), SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, - SyntaxFactory.IdentifierName("this.actorProxy"), + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + SyntaxFactory.IdentifierName("actorProxy")), SyntaxFactory.IdentifierName("actorProxy")) ), }))); From 5bb1a2b23bd5df709d62bfb16b8ea9602e6f6a78 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Tue, 30 Jul 2024 15:47:41 +0200 Subject: [PATCH 38/63] Improved unit test Signed-off-by: Manuel Menegazzo --- .../Helpers/SyntaxFactoryHelpersTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index 8dfde33cd..adb471723 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -51,7 +51,7 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu [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, SyntaxKind[] expectedSyntaxKinds) + public void GetSyntaxKinds_GenerateSyntaxForGivenAccessibility(Accessibility accessibility, ICollection expectedSyntaxKinds) { // Arrange @@ -63,6 +63,8 @@ public void GetSyntaxKinds_GenerateSyntaxForGivenAccessibility(Accessibility acc { Assert.Contains(expectedSyntaxKind, generatedSyntaxKinds); } + + Assert.Equal(expectedSyntaxKinds.Count, generatedSyntaxKinds.Count); } } } From d0c4260b279f051be09c8897515ab7b0d185588a Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 30 Jul 2024 20:42:50 +0200 Subject: [PATCH 39/63] Added implementation of method generation Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 114 +++++++++++------- 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index e1a9e3c0d..95d6186fc 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -216,15 +216,18 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( diagnostics.Add(MethodMustOnlyHaveASingleArgumentOptionallyFollowedByACancellationToken.CreateDiagnostic(method)); } - // If there are any diagnostics, throw an exception to report them. + // If there are any diagnostics, throw an exception to report them and stop the generation. if (diagnostics.Any()) { throw new DiagnosticsException(diagnostics); } + // Get the ActorMethodAttribute data for the method, if it exists. var attributeData = method.GetAttributes() .SingleOrDefault(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); + // Generate the method name to use for the Dapr actor method invocation, using the Name the 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; var methodModifiers = new List() @@ -243,50 +246,73 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( { methodParameters = methodParameters.Append( SyntaxFactory.Parameter(SyntaxFactory.Identifier(cancellationTokenParameter.Name)) - .WithDefault(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression))) - .WithType(SyntaxFactory.ParseTypeName(cancellationTokenParameter.Type.ToString()))); + .WithDefault(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression))) + .WithType(SyntaxFactory.ParseTypeName(cancellationTokenParameter.Type.ToString()))); } - var methodxx = 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[] - { - //SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax("actorProxy"), - //SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( - // SyntaxKind.SimpleAssignmentExpression, - // SyntaxFactory.IdentifierName("this.actorProxy"), - // SyntaxFactory.IdentifierName("actorProxy")) - //), - }))); - - return methodxx; - - //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) - //{ - // argumentDefinitions = argumentDefinitions + " = default"; - //} - - //string argumentList = string.Join(", ", new[] { $@"""{actualMethodName}""" }.Concat(method.Parameters.Select(p => p.Name))); - - //string templateArgs = - // returnTypeArgument is not null - // ? $"<{(requestParameter is not null ? $"{requestParameter.Type}, " : "")}{returnTypeArgument}>" - // : ""; - - //return - //$@"public {method.ReturnType} {method.Name}({argumentDefinitions}) - //{{ - // return this.actorProxy.InvokeMethodAsync{templateArgs}({argumentList}); - //}}"; + var methodReturnType = (INamedTypeSymbol)method.ReturnType; + var methodReturnTypeArguments = methodReturnType.TypeArguments + .Cast() + .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString())); + + if (methodReturnTypeArguments.Any()) + { + 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( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + SyntaxFactory.IdentifierName("actorProxy")), + SyntaxFactory.GenericName( + SyntaxFactory.Identifier("InvokeMethodAsync"), + SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(methodReturnTypeArguments))) + )) + .WithArgumentList(SyntaxFactory.ArgumentList( + SyntaxFactory.SeparatedList(new List() + .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) + .Concat(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenParameter?.Name ?? "default"))) + ) + )) + ), + }))); + + return generatedMethod; + } + else + { + 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( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + SyntaxFactory.IdentifierName("actorProxy")), + SyntaxFactory.IdentifierName("InvokeMethodAsync") + )) + .WithArgumentList(SyntaxFactory.ArgumentList( + SyntaxFactory.SeparatedList(new[] + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName))), + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenParameter?.Name ?? "default")) + })) + ) + ), + }))); + + return generatedMethod; + } } } From 6692c6a94911a3729a539a7e48ca1ec735dd827a Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Thu, 1 Aug 2024 14:41:37 +0200 Subject: [PATCH 40/63] Fixed ArgumentNullException invocation Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 17 ++++++++----- .../Helpers/SyntaxFactoryHelpers.cs | 25 ++++++++++++++++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 95d6186fc..06bf7edd0 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -255,6 +255,10 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( .Cast() .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString())); + var proxyInvocationArguments = method.Parameters + .Where(p => p.Type is not INamedTypeSymbol { Name: "CancellationToken" }) + .Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name))); + if (methodReturnTypeArguments.Any()) { var generatedMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(method.ReturnType.ToString()), method.Name) @@ -277,6 +281,7 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( .WithArgumentList(SyntaxFactory.ArgumentList( SyntaxFactory.SeparatedList(new List() .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) + .Concat(proxyInvocationArguments) .Concat(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenParameter?.Name ?? "default"))) ) )) @@ -303,12 +308,12 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( SyntaxFactory.IdentifierName("InvokeMethodAsync") )) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SeparatedList(new[] - { - SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName))), - SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenParameter?.Name ?? "default")) - })) - ) + SyntaxFactory.SeparatedList(new List() + .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) + .Concat(proxyInvocationArguments) + .Concat( SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenParameter?.Name ?? "default"))) + ) + )) ), }))); diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 588e90447..b09ddeb7a 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -23,7 +23,7 @@ public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argu SyntaxFactory.ParseTypeName("System.ArgumentNullException"), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { - SyntaxFactory.Argument(SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(argumentName))) + SyntaxFactory.Argument(NameOfExpression(argumentName)) })), default ) @@ -50,6 +50,29 @@ public static IfStatementSyntax ThrowIfArgumentNullSyntax(string argumentName) ); } + /// + /// Generates a syntax for nameof expression for the given argument name. + /// + /// + /// + 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)) + })) + ); + } + /// /// Returns the syntax kinds for the specified accessibility. /// From 531adbb0c490acd9fad2a5a208cdca17b016c976 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Thu, 1 Aug 2024 14:43:54 +0200 Subject: [PATCH 41/63] Added test for NameOfExpression Signed-off-by: Manuel Menegazzo --- .../Helpers/SyntaxFactoryHelpersTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index adb471723..2725c5a6f 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -45,6 +45,22 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu Assert.Equal(expectedSource, generatedSource); } + [Fact] + public void NameOfExpression() + { + // Arrange + var argumentName = "arg0"; + var expectedSource = $@"nameof(arg0)"; + + // Act + var generatedSource = SyntaxFactoryHelpers.NameOfExpression(argumentName) + .NormalizeWhitespace() + .ToFullString(); + + // Assert + Assert.Equal(expectedSource, generatedSource); + } + [Theory] [InlineData(Accessibility.Public, new[] { SyntaxKind.PublicKeyword })] [InlineData(Accessibility.Internal, new[] { SyntaxKind.InternalKeyword })] From 36809a00a8b7f86faf54c4fb17202c5c8d83a80e Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Thu, 1 Aug 2024 14:52:08 +0200 Subject: [PATCH 42/63] Fixed ActorProxy method invocation Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 06bf7edd0..a4991cd99 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -256,7 +256,6 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString())); var proxyInvocationArguments = method.Parameters - .Where(p => p.Type is not INamedTypeSymbol { Name: "CancellationToken" }) .Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name))); if (methodReturnTypeArguments.Any()) @@ -282,7 +281,6 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( SyntaxFactory.SeparatedList(new List() .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) .Concat(proxyInvocationArguments) - .Concat(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenParameter?.Name ?? "default"))) ) )) ), @@ -311,7 +309,6 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( SyntaxFactory.SeparatedList(new List() .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) .Concat(proxyInvocationArguments) - .Concat( SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenParameter?.Name ?? "default"))) ) )) ), From cf34718aa2b6269272b978ad5b39450ef47a0ec4 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 08:37:35 +0200 Subject: [PATCH 43/63] Simplified proxy argument definition Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index a4991cd99..349d0c62d 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -255,8 +255,9 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( .Cast() .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString())); - var proxyInvocationArguments = method.Parameters - .Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name))); + var proxyInvocationArguments = new List() + .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) + .Concat(method.Parameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name)))); if (methodReturnTypeArguments.Any()) { @@ -277,7 +278,7 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( SyntaxFactory.Identifier("InvokeMethodAsync"), SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(methodReturnTypeArguments))) )) - .WithArgumentList(SyntaxFactory.ArgumentList( + .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))) SyntaxFactory.SeparatedList(new List() .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) .Concat(proxyInvocationArguments) @@ -305,7 +306,7 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( SyntaxFactory.IdentifierName("actorProxy")), SyntaxFactory.IdentifierName("InvokeMethodAsync") )) - .WithArgumentList(SyntaxFactory.ArgumentList( + .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))) SyntaxFactory.SeparatedList(new List() .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) .Concat(proxyInvocationArguments) From 493cbeccb224921e2b6017693b56b1f8f5e5d713 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 08:59:22 +0200 Subject: [PATCH 44/63] Explicit generic arguments of the proxy call during generation Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 29 +++++++++---------- .../ActorClientGeneratorTests.cs | 6 ++-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 349d0c62d..153661054 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -251,15 +251,24 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( } var methodReturnType = (INamedTypeSymbol)method.ReturnType; - var methodReturnTypeArguments = methodReturnType.TypeArguments - .Cast() - .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString())); + // Define the type arguments to pass to the actor proxy method invocation. + var proxyInvocationTypeArguments = new List() + .Concat(method.Parameters + .Where(p => p.Type is not INamedTypeSymbol { Name: "CancellationToken" }) + .Select(p => SyntaxFactory.ParseTypeName(p.Type.ToString()))) + .Concat(methodReturnType.TypeArguments + .Cast() + .Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString()))); + + // Define the arguments to pass to the actor proxy method invocation. var proxyInvocationArguments = new List() + // Name of remove method to invoke. .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) + // Actor method arguments, including the CancellationToken if it exists. .Concat(method.Parameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name)))); - if (methodReturnTypeArguments.Any()) + if (proxyInvocationTypeArguments.Any()) { var generatedMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(method.ReturnType.ToString()), method.Name) .WithModifiers(SyntaxFactory.TokenList(methodModifiers)) @@ -276,14 +285,9 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( SyntaxFactory.IdentifierName("actorProxy")), SyntaxFactory.GenericName( SyntaxFactory.Identifier("InvokeMethodAsync"), - SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(methodReturnTypeArguments))) + SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(proxyInvocationTypeArguments))) )) .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))) - SyntaxFactory.SeparatedList(new List() - .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) - .Concat(proxyInvocationArguments) - ) - )) ), }))); @@ -307,11 +311,6 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( SyntaxFactory.IdentifierName("InvokeMethodAsync") )) .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))) - SyntaxFactory.SeparatedList(new List() - .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) - .Concat(proxyInvocationArguments) - ) - )) ), }))); diff --git a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs index 24e8dba23..4c0ef194e 100644 --- a/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs +++ b/test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs @@ -337,7 +337,7 @@ public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy) public System.Threading.Tasks.Task TestMethod(Test.TestValue value) { - return this.actorProxy.InvokeMethodAsync(""TestMethod"", value); + return this.actorProxy.InvokeMethodAsync(""TestMethod"", value); } } }"; @@ -564,7 +564,7 @@ public TestActorClient(Dapr.Actors.Client.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); } } }"; @@ -611,7 +611,7 @@ public TestActorClient(Dapr.Actors.Client.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); } } }"; From 0c287f11c9ede930ced64ad56ac93a97728b47e3 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 09:09:15 +0200 Subject: [PATCH 45/63] Handled cancellation token with default value Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 153661054..261fe8ed9 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -239,15 +239,23 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( .Select(p => SyntaxFactory.Parameter(SyntaxFactory.Identifier(p.Name)).WithType(SyntaxFactory.ParseTypeName(p.Type.ToString()))); // Append the CancellationToken parameter if it exists. - if (cancellationTokenParameter is not null - && cancellationTokenParameter.IsOptional - && cancellationTokenParameter.HasExplicitDefaultValue - && cancellationTokenParameter.ExplicitDefaultValue is null) + if (cancellationTokenParameter is not null) { - methodParameters = methodParameters.Append( - SyntaxFactory.Parameter(SyntaxFactory.Identifier(cancellationTokenParameter.Name)) - .WithDefault(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression))) - .WithType(SyntaxFactory.ParseTypeName(cancellationTokenParameter.Type.ToString()))); + if (cancellationTokenParameter.IsOptional + && cancellationTokenParameter.HasExplicitDefaultValue + && cancellationTokenParameter.ExplicitDefaultValue is null) + { + 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()))); + } } var methodReturnType = (INamedTypeSymbol)method.ReturnType; From ad2c22d76c052a68a88d4336e2d977cae34ff692 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 09:13:06 +0200 Subject: [PATCH 46/63] Fixed typo Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 261fe8ed9..aa05db642 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -271,7 +271,7 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( // Define the arguments to pass to the actor proxy method invocation. var proxyInvocationArguments = new List() - // Name of remove method to invoke. + // Name of remote method to invoke. .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) // Actor method arguments, including the CancellationToken if it exists. .Concat(method.Parameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name)))); From 4d336c86c258215c10179892bc77de6dc1da4f77 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 09:44:10 +0200 Subject: [PATCH 47/63] Configured eol used in NormalizeWhitespace function Signed-off-by: Manuel Menegazzo --- .../Helpers/SyntaxFactoryHelpersTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index 2725c5a6f..fa6d91972 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -17,7 +17,7 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullExceptionSyntax(argumentName)) .SyntaxTree .GetRoot() - .NormalizeWhitespace() + .NormalizeWhitespace(eol: "\r\n") .ToFullString(); // Assert @@ -38,7 +38,7 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax(argumentName) .SyntaxTree .GetRoot() - .NormalizeWhitespace() + .NormalizeWhitespace(eol: "\r\n") .ToFullString(); // Assert @@ -54,7 +54,7 @@ public void NameOfExpression() // Act var generatedSource = SyntaxFactoryHelpers.NameOfExpression(argumentName) - .NormalizeWhitespace() + .NormalizeWhitespace(eol: "\r\n") .ToFullString(); // Assert From 3bc0674c653fb84faf266e3acb8026e90919a9e6 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 10:31:48 +0200 Subject: [PATCH 48/63] Normalized expected source Signed-off-by: Manuel Menegazzo --- .../Helpers/SyntaxFactoryHelpersTests.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index fa6d91972..1726d6276 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -12,16 +12,20 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS // 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.ThrowArgumentNullExceptionSyntax(argumentName)) .SyntaxTree .GetRoot() - .NormalizeWhitespace(eol: "\r\n") + .NormalizeWhitespace() .ToFullString(); // Assert - Assert.Equal(expectedSource, generatedSource); + Assert.Equal(expectedSourceNormalized, generatedSource); } [Fact] @@ -33,16 +37,20 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu {{ throw new System.ArgumentNullException(nameof(arg0)); }}"; + var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource) + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); // Act var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax(argumentName) .SyntaxTree .GetRoot() - .NormalizeWhitespace(eol: "\r\n") + .NormalizeWhitespace() .ToFullString(); // Assert - Assert.Equal(expectedSource, generatedSource); + Assert.Equal(expectedSourceNormalized, generatedSource); } [Fact] @@ -51,14 +59,20 @@ 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) - .NormalizeWhitespace(eol: "\r\n") + .SyntaxTree + .GetRoot() + .NormalizeWhitespace() .ToFullString(); // Assert - Assert.Equal(expectedSource, generatedSource); + Assert.Equal(expectedSourceNormalized, generatedSource); } [Theory] From c0f9c4707ee8e21461b3971b4d81b8c73e836883 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 10:55:27 +0200 Subject: [PATCH 49/63] Moved to constat the ActorProxyTypeName Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 7 +++++-- src/Dapr.Actors.Generators/Constants.cs | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index aa05db642..670d277e3 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -116,8 +116,10 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient 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); - var actorProxyField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) + // Generate the actor proxy field to store the actor proxy instance. + var actorProxyField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(actorProxyTypeSyntax) .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) .WithModifiers(SyntaxFactory.TokenList(new[] { @@ -125,11 +127,12 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient 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(SyntaxFactory.ParseTypeName("Dapr.Actors.Client.ActorProxy")) + SyntaxFactory.Parameter(SyntaxFactory.Identifier("actorProxy")).WithType(actorProxyTypeSyntax) }))) .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { diff --git a/src/Dapr.Actors.Generators/Constants.cs b/src/Dapr.Actors.Generators/Constants.cs index c00f04bb6..392def4ef 100644 --- a/src/Dapr.Actors.Generators/Constants.cs +++ b/src/Dapr.Actors.Generators/Constants.cs @@ -29,5 +29,10 @@ internal static class Constants /// 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"; } } From 7a86b4f2043377f3c8e8ce611a737a85bce7cfbc Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Fri, 2 Aug 2024 11:27:09 +0200 Subject: [PATCH 50/63] Fix typo Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 670d277e3..147acb102 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -119,7 +119,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient var actorProxyTypeSyntax = SyntaxFactory.ParseTypeName(Constants.ActorProxyTypeName); // Generate the actor proxy field to store the actor proxy instance. - var actorProxyField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(actorProxyTypeSyntax) + var actorProxyFieldDeclaration = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(actorProxyTypeSyntax) .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("actorProxy"))))) .WithModifiers(SyntaxFactory.TokenList(new[] { @@ -153,10 +153,9 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient .Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol)); var actorMembers = new List() - .Concat(actorProxyField) + .Concat(actorProxyFieldDeclaration) .Concat(actorCtor) - .Concat(actorMethods) - .ToList(); + .Concat(actorMethods); var actorClientClassModifiers = new List() .Concat(SyntaxFactoryHelpers.GetSyntaxKinds(descriptor.Accessibility)) @@ -237,11 +236,13 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( .Concat(SyntaxFactoryHelpers.GetSyntaxKinds(method.DeclaredAccessibility)) .Select(sk => SyntaxFactory.Token(sk)); + // 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()))); - // Append the CancellationToken parameter if it exists. + // 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 (cancellationTokenParameter.IsOptional From c5f5f1108374dcdc1b54ab47e5c00393a6cc8df8 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Thu, 8 Aug 2024 13:34:31 +0200 Subject: [PATCH 51/63] Created ActorProxyInvokeMethodAsync SyntaxFactoryHelper Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 87 +++++-------------- .../Helpers/SyntaxFactoryHelpers.cs | 57 +++++++++++- .../Helpers/SyntaxFactoryHelpersTests.cs | 41 ++++++++- 3 files changed, 111 insertions(+), 74 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 147acb102..b4cf4f098 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -136,7 +136,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient }))) .WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { - SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax("actorProxy"), + SyntaxFactoryHelpers.ThrowIfArgumentNull("actorProxy"), SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.MemberAccessExpression( @@ -228,7 +228,7 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( var attributeData = method.GetAttributes() .SingleOrDefault(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); - // Generate the method name to use for the Dapr actor method invocation, using the Name the property of ActorMethodAttribute if specified, + // 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; @@ -262,71 +262,26 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( } } + // Extract the return type of the method. var methodReturnType = (INamedTypeSymbol)method.ReturnType; - // Define the type arguments to pass to the actor proxy method invocation. - var proxyInvocationTypeArguments = new List() - .Concat(method.Parameters - .Where(p => p.Type is not INamedTypeSymbol { Name: "CancellationToken" }) - .Select(p => SyntaxFactory.ParseTypeName(p.Type.ToString()))) - .Concat(methodReturnType.TypeArguments - .Cast() - .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. - .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName)))) - // Actor method arguments, including the CancellationToken if it exists. - .Concat(method.Parameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name)))); - - if (proxyInvocationTypeArguments.Any()) - { - 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( - SyntaxFactory.InvocationExpression( - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.ThisExpression(), - SyntaxFactory.IdentifierName("actorProxy")), - SyntaxFactory.GenericName( - SyntaxFactory.Identifier("InvokeMethodAsync"), - SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(proxyInvocationTypeArguments))) - )) - .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))) - ), - }))); - - return generatedMethod; - } - else - { - 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( - SyntaxFactory.InvocationExpression( - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.ThisExpression(), - SyntaxFactory.IdentifierName("actorProxy")), - SyntaxFactory.IdentifierName("InvokeMethodAsync") - )) - .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments))) - ), - }))); - - return generatedMethod; - } + // 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; } } diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index b09ddeb7a..a1d2a23fd 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -1,4 +1,5 @@ -using Microsoft.CodeAnalysis; +using Dapr.Actors.Generators.Extensions; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -14,7 +15,7 @@ public static partial class SyntaxFactoryHelpers /// /// /// - public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argumentName) + public static ThrowExpressionSyntax ThrowArgumentNullException(string argumentName) { return SyntaxFactory.ThrowExpression( SyntaxFactory.Token(SyntaxKind.ThrowKeyword), @@ -35,7 +36,7 @@ public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argu /// /// /// - public static IfStatementSyntax ThrowIfArgumentNullSyntax(string argumentName) + public static IfStatementSyntax ThrowIfArgumentNull(string argumentName) { return SyntaxFactory.IfStatement( SyntaxFactory.BinaryExpression( @@ -45,7 +46,7 @@ public static IfStatementSyntax ThrowIfArgumentNullSyntax(string argumentName) ), SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[] { - SyntaxFactory.ExpressionStatement(ThrowArgumentNullExceptionSyntax(argumentName)) + SyntaxFactory.ExpressionStatement(ThrowArgumentNullException(argumentName)) })) ); } @@ -73,6 +74,54 @@ public static ExpressionSyntax NameOfExpression(string argumentName) ); } + /// + /// Generates the invocation syntax to call a remote method with the actor proxy. + /// + /// Memeber syntax to access actorProxy member. + /// Name of remote method to invoke. + /// Remote method parameters. + /// Return types of remote method invocation. + /// + 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. + .Concat(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 syntax kinds for the specified accessibility. /// diff --git a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs index 1726d6276..807bd7469 100644 --- a/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs +++ b/test/Dapr.Actors.Generators.Test/Helpers/SyntaxFactoryHelpersTests.cs @@ -7,7 +7,7 @@ namespace Dapr.Actors.Generators.Test.Helpers public class SyntaxFactoryHelpersTests { [Fact] - public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionSyntaxWithGivenArgumentName() + public void ThrowArgumentNullException_GenerateThrowArgumentNullExceptionSyntaxWithGivenArgumentName() { // Arrange var argumentName = "arg0"; @@ -18,7 +18,7 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS .ToFullString(); // Act - var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullExceptionSyntax(argumentName)) + var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullException(argumentName)) .SyntaxTree .GetRoot() .NormalizeWhitespace() @@ -29,7 +29,7 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS } [Fact] - public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgumentName() + public void ThrowIfArgumentNullException_GivesNullCheckSyntaxWithGivenArgumentName() { // Arrange var argumentName = "arg0"; @@ -43,7 +43,7 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu .ToFullString(); // Act - var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax(argumentName) + var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNull(argumentName) .SyntaxTree .GetRoot() .NormalizeWhitespace() @@ -53,6 +53,39 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu 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() { From 51ed60d011ae57a71df4357857b200ae9fec3d88 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com.> Date: Thu, 8 Aug 2024 13:41:11 +0200 Subject: [PATCH 52/63] Removed custom concat implementation Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 6 +++--- .../Extensions/IEnumerableExtensions.cs | 12 ------------ .../Helpers/SyntaxFactoryHelpers.cs | 5 ++--- .../Extensions/IEnumerableExtensionsTests.cs | 14 -------------- 4 files changed, 5 insertions(+), 32 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index b4cf4f098..50c022a9f 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -153,13 +153,13 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient .Select(member => GenerateMethodImplementation(member, actorMethodAttributeSymbol, cancellationTokenSymbol)); var actorMembers = new List() - .Concat(actorProxyFieldDeclaration) - .Concat(actorCtor) + .Append(actorProxyFieldDeclaration) + .Append(actorCtor) .Concat(actorMethods); var actorClientClassModifiers = new List() .Concat(SyntaxFactoryHelpers.GetSyntaxKinds(descriptor.Accessibility)) - .Concat(SyntaxKind.SealedKeyword) + .Append(SyntaxKind.SealedKeyword) .Select(sk => SyntaxFactory.Token(sk)); var actorClientClassDeclaration = SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName) diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index 844a088fc..e9c093f96 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -30,17 +30,5 @@ internal static int IndexOf(this IEnumerable source, Func predica return -1; } - - /// - /// Concatenates the specified item to the end of the sequence. - /// - /// - /// - /// - /// - internal static IEnumerable Concat(this IEnumerable source, T item) - { - return source.Concat(new[] { item }); - } } } diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index a1d2a23fd..34f0604fb 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -1,5 +1,4 @@ -using Dapr.Actors.Generators.Extensions; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -99,7 +98,7 @@ public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync( // Define the arguments to pass to the actor proxy method invocation. var proxyInvocationArguments = new List() // Name of remote method to invoke. - .Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(remoteMethodName)))) + .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)))); diff --git a/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs index d753eb000..97dbcfe1e 100644 --- a/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs +++ b/test/Dapr.Actors.Generators.Test/Extensions/IEnumerableExtensionsTests.cs @@ -48,19 +48,5 @@ public void IndexOf_WhenItemExists_ReturnsIndexOfItem(int[] source, int item, in // Assert Assert.Equal(expected, index); } - - [Fact] - public void Concat_WhenItemIsNotNull_ReturnsConcatenatedSequence() - { - // Arrange - var source = new[] { "a", "b", "c" }; - string item = "d"; - - // Act - var result = source.Concat(item); - - // Assert - Assert.Equal(new[] { "a", "b", "c", "d" }, result); - } } } From f8b31999225739b07e3a3c569ae47274b8316acf Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:26:24 -0400 Subject: [PATCH 53/63] fix (#1329) Signed-off-by: Hannah Hunter Signed-off-by: Manuel Menegazzo --- daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md index f608cd07d..09aed4c8f 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md @@ -56,7 +56,7 @@ Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance) #### gRPC You can use the `DaprClient` to invoke your services over gRPC. -{{% codetab %}} + ```csharp using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); var invoker = DaprClient.CreateInvocationInvoker(appId: myAppId, daprEndpoint: serviceEndpoint); @@ -67,8 +67,6 @@ await client.MyMethodAsync(new Empty(), options); Assert.Equal(StatusCode.DeadlineExceeded, ex.StatusCode); ``` -{{% /codetab %}} - - For a full guide on service invocation visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}). @@ -162,7 +160,7 @@ var secrets = await client.GetSecretAsync("mysecretstore", "key-value-pair-secre Console.WriteLine($"Got secret keys: {string.Join(", ", secrets.Keys)}"); ``` -{{% / codetab %}} +{{% /codetab %}} {{% codetab %}} From acfbfe7ee06c1e11a6ce4c63a6ae20c3ceeb2b15 Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Fri, 9 Aug 2024 00:37:27 -0400 Subject: [PATCH 54/63] link to non-dapr endpoint howto (#1335) Signed-off-by: Hannah Hunter Signed-off-by: Manuel Menegazzo --- daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md index 09aed4c8f..cab44468b 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md @@ -24,6 +24,12 @@ The .NET SDK allows you to interface with all of the [Dapr building blocks]({{< #### HTTP You can either use the `DaprClient` or `System.Net.Http.HttpClient` to invoke your services. +{{% alert title="Note" color="primary" %}} + You can also [invoke a non-Dapr endpoint using either a named `HTTPEndpoint` or an FQDN URL to the non-Dapr environment]({{< ref "howto-invoke-non-dapr-endpoints.md#using-an-httpendpoint-resource-or-fqdn-url-for-non-dapr-endpoints" >}}). + +{{% /alert %}} + + {{< tabs SDK HTTP>}} {{% codetab %}} From 2d4fe2f3a66aae18c210128abfb71b656cf567a7 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Wed, 21 Aug 2024 23:47:39 -0700 Subject: [PATCH 55/63] Merge 1.14 release branch back into `master`. (#1337) Signed-off-by: Manuel Menegazzo --- .devcontainer/localinit.sh | 2 +- .github/workflows/itests.yml | 6 +- daprdocs/content/en/dotnet-sdk-docs/_index.md | 2 +- .../dotnet-actors/dotnet-actors-howto.md | 2 +- examples/Actor/README.md | 4 +- .../BulkPublishEventExample/README.md | 2 +- .../PublishEventExample/README.md | 2 +- samples/Client/README.md | 2 +- .../Protos/dapr/proto/common/v1/common.proto | 2 +- .../dapr/proto/dapr/v1/appcallback.proto | 32 ++- .../Protos/dapr/proto/dapr/v1/dapr.proto | 187 ++++++++++++++++-- 11 files changed, 212 insertions(+), 31 deletions(-) diff --git a/.devcontainer/localinit.sh b/.devcontainer/localinit.sh index 80b27e4f4..69c4c1274 100644 --- a/.devcontainer/localinit.sh +++ b/.devcontainer/localinit.sh @@ -6,4 +6,4 @@ az extension add --name containerapp --yes nvm install v18.12.1 # initialize Dapr -dapr init --runtime-version=1.10.0-rc.2 \ No newline at end of file +dapr init --runtime-version=1.14.0 \ No newline at end of file diff --git a/.github/workflows/itests.yml b/.github/workflows/itests.yml index 00121c9f5..36741ce7c 100644 --- a/.github/workflows/itests.yml +++ b/.github/workflows/itests.yml @@ -42,9 +42,9 @@ jobs: GOOS: linux GOARCH: amd64 GOPROXY: https://proxy.golang.org - DAPR_CLI_VER: 1.13.0 - DAPR_RUNTIME_VER: 1.13.2 - DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.12/install/install.sh + DAPR_CLI_VER: 1.14.0 + DAPR_RUNTIME_VER: 1.14.0 + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh DAPR_CLI_REF: '' steps: - name: Set up Dapr CLI diff --git a/daprdocs/content/en/dotnet-sdk-docs/_index.md b/daprdocs/content/en/dotnet-sdk-docs/_index.md index e823ca29f..121dde310 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/_index.md @@ -18,7 +18,7 @@ Dapr offers a variety of packages to help with the development of .NET applicati - [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed - Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}) -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed ## Installation diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md index 8229d6820..eaa13625d 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md @@ -45,7 +45,7 @@ This project contains the implementation of the actor client which calls MyActor - [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed. - Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}). -- [.NET Core 3.1 or .NET 6+](https://dotnet.microsoft.com/download) installed. Dapr .NET SDK uses [ASP.NET Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-6.0). +- [.NET 6+](https://dotnet.microsoft.com/download) installed. Dapr .NET SDK uses [ASP.NET Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-6.0). ## Step 0: Prepare diff --git a/examples/Actor/README.md b/examples/Actor/README.md index a7bb46c03..89b6bf0bb 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -4,7 +4,7 @@ The Actor example shows how to create a virtual actor (`DemoActor`) and invoke i ## Prerequisites -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) - [Dapr .NET SDK](https://github.com/dapr/dotnet-sdk/) @@ -102,7 +102,7 @@ docker push /demo-actor:latest ### Deploy the Actor service to Kubernetes #### Prerequisites - A Kubernetes cluster with `kubectl` configured to access it. -- Dapr v1.13+ installed on the Kubernetes cluster. Follow the instructions [here](https://docs.dapr.io/getting-started/install-dapr-kubernetes/). +- Dapr v1.14+ installed on the Kubernetes cluster. Follow the instructions [here](https://docs.dapr.io/getting-started/install-dapr-kubernetes/). - A Docker registry where you pushed the `DemoActor` image. #### Deploy the Actor service diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md b/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md index dfcc99ca6..39d206fa2 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md @@ -2,7 +2,7 @@ ## Prerequisites -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) - [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/) diff --git a/examples/Client/PublishSubscribe/PublishEventExample/README.md b/examples/Client/PublishSubscribe/PublishEventExample/README.md index 455fc2537..9f3af565f 100644 --- a/examples/Client/PublishSubscribe/PublishEventExample/README.md +++ b/examples/Client/PublishSubscribe/PublishEventExample/README.md @@ -2,7 +2,7 @@ ## Prerequisites -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) - [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/) diff --git a/samples/Client/README.md b/samples/Client/README.md index 2501bfadc..2bb738d89 100644 --- a/samples/Client/README.md +++ b/samples/Client/README.md @@ -8,7 +8,7 @@ The following examples will show you how to: ## Prerequisites -* [.Net Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) +* [.NET 6+](https://dotnet.microsoft.com/download) * [Dapr CLI](https://github.com/dapr/cli) * [Dapr DotNet SDK](https://github.com/dapr/dotnet-sdk) diff --git a/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto b/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto index 1e63b885d..4acf9159d 100644 --- a/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto +++ b/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto @@ -157,4 +157,4 @@ message ConfigurationItem { // the metadata which will be passed to/from configuration store component. map metadata = 3; -} +} \ No newline at end of file diff --git a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto index 823c0aae4..a86040364 100644 --- a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto +++ b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto @@ -15,6 +15,7 @@ syntax = "proto3"; package dapr.proto.runtime.v1; +import "google/protobuf/any.proto"; import "google/protobuf/empty.proto"; import "dapr/proto/common/v1/common.proto"; import "google/protobuf/struct.proto"; @@ -59,8 +60,37 @@ service AppCallbackHealthCheck { service AppCallbackAlpha { // Subscribes bulk events from Pubsub rpc OnBulkTopicEventAlpha1(TopicEventBulkRequest) returns (TopicEventBulkResponse) {} + + // Sends job back to the app's endpoint at trigger time. + rpc OnJobEventAlpha1 (JobEventRequest) returns (JobEventResponse); +} + +message JobEventRequest { + // Job name. + string name = 1; + + // Job data to be sent back to app. + google.protobuf.Any data = 2; + + // Required. method is a method name which will be invoked by caller. + string method = 3; + + // The type of data content. + // + // This field is required if data delivers http request body + // Otherwise, this is optional. + string content_type = 4; + + // HTTP specific fields if request conveys http-compatible request. + // + // This field is required for http-compatible request. Otherwise, + // this field is optional. + common.v1.HTTPExtension http_extension = 5; } +// JobEventResponse is the response from the app when a job is triggered. +message JobEventResponse {} + // TopicEventRequest message is compatible with CloudEvent spec v1.0 // https://github.com/cloudevents/spec/blob/v1.0/spec.md message TopicEventRequest { @@ -310,4 +340,4 @@ message ListInputBindingsResponse { // HealthCheckResponse is the message with the response to the health check. // This message is currently empty as used as placeholder. -message HealthCheckResponse {} +message HealthCheckResponse {} \ No newline at end of file diff --git a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto index 5ec1cc9d8..4185fb391 100644 --- a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto +++ b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto @@ -19,6 +19,7 @@ import "google/protobuf/any.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; import "dapr/proto/common/v1/common.proto"; +import "dapr/proto/dapr/v1/appcallback.proto"; option csharp_namespace = "Dapr.Client.Autogen.Grpc.v1"; option java_outer_classname = "DaprProtos"; @@ -58,6 +59,10 @@ service Dapr { // Bulk Publishes multiple events to the specified topic. rpc BulkPublishEventAlpha1(BulkPublishRequest) returns (BulkPublishResponse) {} + // SubscribeTopicEventsAlpha1 subscribes to a PubSub topic and receives topic + // events from it. + rpc SubscribeTopicEventsAlpha1(stream SubscribeTopicEventsRequestAlpha1) returns (stream TopicEventRequest) {} + // Invokes binding data to specific output bindings rpc InvokeBinding(InvokeBindingRequest) returns (InvokeBindingResponse) {} @@ -188,6 +193,15 @@ service Dapr { rpc RaiseEventWorkflowBeta1 (RaiseEventWorkflowRequest) returns (google.protobuf.Empty) {} // Shutdown the sidecar rpc Shutdown (ShutdownRequest) returns (google.protobuf.Empty) {} + + // Create and schedule a job + rpc ScheduleJobAlpha1(ScheduleJobRequest) returns (ScheduleJobResponse) {} + + // Gets a scheduled job + rpc GetJobAlpha1(GetJobRequest) returns (GetJobResponse) {} + + // Delete a job + rpc DeleteJobAlpha1(DeleteJobRequest) returns (DeleteJobResponse) {} } // InvokeServiceRequest represents the request message for Service invocation. @@ -411,6 +425,47 @@ message BulkPublishResponseFailedEntry { string error = 2; } +// SubscribeTopicEventsRequestAlpha1 is a message containing the details for +// subscribing to a topic via streaming. +// The first message must always be the initial request. All subsequent +// messages must be event responses. +message SubscribeTopicEventsRequestAlpha1 { + oneof subscribe_topic_events_request_type { + SubscribeTopicEventsInitialRequestAlpha1 initial_request = 1; + SubscribeTopicEventsResponseAlpha1 event_response = 2; + } +} + +// SubscribeTopicEventsInitialRequestAlpha1 is the initial message containing the +// details for subscribing to a topic via streaming. +message SubscribeTopicEventsInitialRequestAlpha1 { + // The name of the pubsub component + string pubsub_name = 1; + + // The pubsub topic + string topic = 2; + + // The metadata passing to pub components + // + // metadata property: + // - key : the key of the message. + map metadata = 3; + + // dead_letter_topic is the topic to which messages that fail to be processed + // are sent. + optional string dead_letter_topic = 4; +} + +// SubscribeTopicEventsResponseAlpha1 is a message containing the result of a +// subscription to a topic. +message SubscribeTopicEventsResponseAlpha1 { + // id is the unique identifier for the subscription request. + string id = 1; + + // status is the result of the subscription request. + TopicEventResponse status = 2; +} + // InvokeBindingRequest is the message to send data to output bindings message InvokeBindingRequest { // The name of the output binding to invoke. @@ -504,45 +559,45 @@ message ExecuteStateTransactionRequest { // RegisterActorTimerRequest is the message to register a timer for an actor of a given type and id. message RegisterActorTimerRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; - string due_time = 4; + string due_time = 4 [json_name = "dueTime"]; string period = 5; string callback = 6; - bytes data = 7; + bytes data = 7; string ttl = 8; } // UnregisterActorTimerRequest is the message to unregister an actor timer message UnregisterActorTimerRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; } // RegisterActorReminderRequest is the message to register a reminder for an actor of a given type and id. message RegisterActorReminderRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; - string due_time = 4; + string due_time = 4 [json_name = "dueTime"]; string period = 5; - bytes data = 6; + bytes data = 6; string ttl = 7; } // UnregisterActorReminderRequest is the message to unregister an actor reminder. message UnregisterActorReminderRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; } // GetActorStateRequest is the message to get key-value states from specific actor. message GetActorStateRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string key = 3; } @@ -556,8 +611,8 @@ message GetActorStateResponse { // ExecuteActorStateTransactionRequest is the message to execute multiple operations on a specified actor. message ExecuteActorStateTransactionRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; repeated TransactionalActorStateOperation operations = 3; } @@ -575,8 +630,8 @@ message TransactionalActorStateOperation { // InvokeActorRequest is the message to call an actor. message InvokeActorRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string method = 3; bytes data = 4; map metadata = 5; @@ -605,6 +660,7 @@ message GetMetadataResponse { string runtime_version = 8 [json_name = "runtimeVersion"]; repeated string enabled_features = 9 [json_name = "enabledFeatures"]; ActorRuntime actor_runtime = 10 [json_name = "actorRuntime"]; + //TODO: Cassie: probably add scheduler runtime status } message ActorRuntime { @@ -665,6 +721,19 @@ message PubsubSubscription { map metadata = 3 [json_name = "metadata"]; PubsubSubscriptionRules rules = 4 [json_name = "rules"]; string dead_letter_topic = 5 [json_name = "deadLetterTopic"]; + PubsubSubscriptionType type = 6 [json_name = "type"]; +} + +// PubsubSubscriptionType indicates the type of subscription +enum PubsubSubscriptionType { + // UNKNOWN is the default value for the subscription type. + UNKNOWN = 0; + // Declarative subscription (k8s CRD) + DECLARATIVE = 1; + // Programmatically created subscription + PROGRAMMATIC = 2; + // Bidirectional Streaming subscription + STREAMING = 3; } message PubsubSubscriptionRules { @@ -1108,3 +1177,85 @@ message PurgeWorkflowRequest { message ShutdownRequest { // Empty } + +// Job is the definition of a job. At least one of schedule or due_time must be +// provided but can also be provided together. +message Job { + // The unique name for the job. + string name = 1 [json_name = "name"]; + + // schedule is an optional schedule at which the job is to be run. + // Accepts both systemd timer style cron expressions, as well as human + // readable '@' prefixed period strings as defined below. + // + // Systemd timer style cron accepts 6 fields: + // seconds | minutes | hours | day of month | month | day of week + // 0-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-7/sun-sat + // + // "0 30 * * * *" - every hour on the half hour + // "0 15 3 * * *" - every day at 03:15 + // + // Period string expressions: + // Entry | Description | Equivalent To + // ----- | ----------- | ------------- + // @every | Run every (e.g. '@every 1h30m') | N/A + // @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * + // @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * + // @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 + // @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * + // @hourly | Run once an hour, beginning of hour | 0 0 * * * * + optional string schedule = 2 [json_name = "schedule"]; + + // repeats is the optional number of times in which the job should be + // triggered. If not set, the job will run indefinitely or until expiration. + optional uint32 repeats = 3 [json_name = "repeats"]; + + // due_time is the optional time at which the job should be active, or the + // "one shot" time if other scheduling type fields are not provided. Accepts + // a "point in time" string in the format of RFC3339, Go duration string + // (calculated from job creation time), or non-repeating ISO8601. + optional string due_time = 4 [json_name = "dueTime"]; + + // ttl is the optional time to live or expiration of the job. Accepts a + // "point in time" string in the format of RFC3339, Go duration string + // (calculated from job creation time), or non-repeating ISO8601. + optional string ttl = 5 [json_name = "ttl"]; + + // payload is the serialized job payload that will be sent to the recipient + // when the job is triggered. + google.protobuf.Any data = 6 [json_name = "data"]; +} + +// ScheduleJobRequest is the message to create/schedule the job. +message ScheduleJobRequest { + // The job details. + Job job = 1; +} + +// ScheduleJobResponse is the message response to create/schedule the job. +message ScheduleJobResponse { + // Empty +} + +// GetJobRequest is the message to retrieve a job. +message GetJobRequest { + // The name of the job. + string name = 1; +} + +// GetJobResponse is the message's response for a job retrieved. +message GetJobResponse { + // The job details. + Job job = 1; +} + +// DeleteJobRequest is the message to delete the job by name. +message DeleteJobRequest { + // The name of the job. + string name = 1; +} + +// DeleteJobResponse is the message response to delete the job by name. +message DeleteJobResponse { + // Empty +} From a03321884175bd750fe679e71ad15ffe4b042dd4 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 3 Sep 2024 19:21:20 +0200 Subject: [PATCH 56/63] Fixed merge errors Signed-off-by: Manuel Menegazzo Signed-off-by: Manuel Menegazzo --- all.sln | 5 ++--- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 14 +++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/all.sln b/all.sln index 4a8fd0aca..12c1dca68 100644 --- a/all.sln +++ b/all.sln @@ -32,6 +32,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BD1276E-D28A-45EA-89B1-6AD48471500D}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.Test", "test\Dapr.Actors.AspNetCore.Test\Dapr.Actors.AspNetCore.Test.csproj", "{9C1D6ABA-5EDE-4FA0-A8A9-0AB98CB74737}" @@ -346,6 +347,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} EndGlobalSection -EndGlobal -lobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 50c022a9f..bec1d9706 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -195,10 +195,14 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient /// /// Generates the method implementation for the specified method. /// - /// - /// - /// - /// + /// + /// MethodSymbol extracted from the actor interface representing the method to generate. + /// + /// + /// ActorMethodAttribute symbol used to extract generation the original actor method to use when making runtime calls. + /// + /// Used to check search the position of optional cancellationToken. + /// Returns a of the generated method. private static MethodDeclarationSyntax GenerateMethodImplementation( IMethodSymbol method, INamedTypeSymbol generateActorClientAttributeSymbol, @@ -262,7 +266,7 @@ private static MethodDeclarationSyntax GenerateMethodImplementation( } } - // Extract the return type of the method. + // Extract the return type of the original method. var methodReturnType = (INamedTypeSymbol)method.ReturnType; // Generate the method implementation. From 494383c9b3deea95a0126f90c0a16258f6999149 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 3 Sep 2024 20:46:35 +0200 Subject: [PATCH 57/63] Updated some summaries Signed-off-by: Manuel Menegazzo --- .../ActorClientGenerator.cs | 13 +++++----- .../DiagnosticsException.cs | 12 +++++++++- .../Helpers/SyntaxFactoryHelpers.cs | 24 +++++++++---------- src/Dapr.Actors.Generators/Templates.cs | 16 ++++++------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index bec1d9706..451bba2a3 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -57,10 +57,9 @@ public void Initialize(IncrementalGeneratorInitializationContext 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. static ActorClientDescriptor CreateActorClientDescriptor( GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) @@ -100,9 +99,9 @@ static ActorClientDescriptor CreateActorClientDescriptor( /// /// 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 assembly doesn't one or more required symbols. static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor descriptor) { try diff --git a/src/Dapr.Actors.Generators/DiagnosticsException.cs b/src/Dapr.Actors.Generators/DiagnosticsException.cs index 35d5b9c6c..19d4f0751 100644 --- a/src/Dapr.Actors.Generators/DiagnosticsException.cs +++ b/src/Dapr.Actors.Generators/DiagnosticsException.cs @@ -2,14 +2,24 @@ 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. + /// + /// public DiagnosticsException(IEnumerable diagnostics) : base(string.Join("\n", diagnostics.Select(d => d.ToString()))) { this.Diagnostics = diagnostics.ToArray(); } - public IEnumerable Diagnostics { get; } + /// + /// Diagnostics encountered during code generation. + /// + public ICollection Diagnostics { get; } } } diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 34f0604fb..4996223b9 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -10,10 +10,10 @@ namespace Dapr.Actors.Generators.Helpers public static partial class SyntaxFactoryHelpers { /// - /// Generates a syntax for syntax for the given argument name. + /// Generates a syntax for syntax for 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( @@ -33,8 +33,8 @@ public static ThrowExpressionSyntax ThrowArgumentNullException(string argumentNa /// /// 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( @@ -53,8 +53,8 @@ public static IfStatementSyntax ThrowIfArgumentNull(string 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( @@ -76,11 +76,11 @@ public static ExpressionSyntax NameOfExpression(string argumentName) /// /// Generates the invocation syntax to call a remote method with the actor proxy. /// - /// Memeber syntax to access actorProxy member. + /// Member syntax to access actorProxy member. /// Name of remote method to invoke. /// Remote method parameters. /// Return types of remote method invocation. - /// + /// The representing a call to the actor proxy. public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync( MemberAccessExpressionSyntax actorProxyMemberSyntax, string remoteMethodName, @@ -124,9 +124,9 @@ public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync( /// /// Returns the syntax kinds for the specified accessibility. /// - /// - /// - /// + /// Accessibility to convert into a SyntaxKind. + /// Return the collection of representing the given accessibility. + /// Throws when un unexpected syntax is passed. public static ICollection GetSyntaxKinds(Accessibility accessibility) { var syntaxKinds = new List(); diff --git a/src/Dapr.Actors.Generators/Templates.cs b/src/Dapr.Actors.Generators/Templates.cs index fe393ddb9..a9482a980 100644 --- a/src/Dapr.Actors.Generators/Templates.cs +++ b/src/Dapr.Actors.Generators/Templates.cs @@ -11,11 +11,11 @@ namespace Dapr.Actors.Generators internal static partial class Templates { /// - /// Returns the source text for the ActorMethodAttribute. + /// 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 == null) @@ -47,11 +47,11 @@ internal sealed class {Constants.ActorMethodAttributeTypeName} : Attribute } /// - /// Returns the source text for the GenerateActorClientAttribute. + /// 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 == null) From 89b40692a2d9f78d3b2cf29c466f1acbff592e3c Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 4 Sep 2024 19:19:56 +0200 Subject: [PATCH 58/63] Added some missing summaries Signed-off-by: Manuel Menegazzo --- .../Extensions/IEnumerableExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index e9c093f96..0f5b2ceb6 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -5,9 +5,9 @@ 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. /// - /// - /// - /// + /// Tipe of items. + /// Source enumerable where 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) { From 39b42898f612a39ccb46ebe166815388aabe43a6 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 4 Sep 2024 20:53:14 +0200 Subject: [PATCH 59/63] Fixed typo Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/DiagnosticsException.cs | 2 +- src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Actors.Generators/DiagnosticsException.cs b/src/Dapr.Actors.Generators/DiagnosticsException.cs index 19d4f0751..d196f8484 100644 --- a/src/Dapr.Actors.Generators/DiagnosticsException.cs +++ b/src/Dapr.Actors.Generators/DiagnosticsException.cs @@ -10,7 +10,7 @@ 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()))) { diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index 0f5b2ceb6..3a6122f8d 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -5,7 +5,7 @@ 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. /// - /// Tipe of items. + /// Type of items. /// Source enumerable where 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. From 30bb460ad16140898db869751167e707e3272158 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 4 Sep 2024 21:32:32 +0200 Subject: [PATCH 60/63] Improved some summary text Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 451bba2a3..c0e39fcdf 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -101,7 +101,7 @@ static ActorClientDescriptor CreateActorClientDescriptor( /// /// Context passed from the source generator when it has registered an output. /// Descriptor of actor client to generate. - /// Throws when assembly doesn't one or more required symbols. + /// Throws when one or more required symbols assembly are missing. static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor descriptor) { try @@ -198,9 +198,9 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient /// MethodSymbol extracted from the actor interface representing the method to generate. /// /// - /// ActorMethodAttribute symbol used to extract generation the original actor method to use when making runtime calls. + /// ActorMethodAttribute symbol used to extract the original actor method name to use when making runtime calls. /// - /// Used to check search the position of optional cancellationToken. + /// Symbol used to search the position of cancellationToken between method parameters. /// Returns a of the generated method. private static MethodDeclarationSyntax GenerateMethodImplementation( IMethodSymbol method, From 6b219d2cdcf5d4e18bc90abd6965739006e3b176 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 4 Sep 2024 21:43:07 +0200 Subject: [PATCH 61/63] Improved summaries Signed-off-by: Manuel Menegazzo --- .../Extensions/IEnumerableExtensions.cs | 4 ++-- .../Helpers/SyntaxFactoryHelpers.cs | 14 +++++++------- .../Models/ActorClientDescriptor.cs | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs index 3a6122f8d..6b45e86f3 100644 --- a/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs +++ b/src/Dapr.Actors.Generators/Extensions/IEnumerableExtensions.cs @@ -5,8 +5,8 @@ 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. /// - /// Type of items. - /// Source enumerable where to search. + /// 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) diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 4996223b9..19d5ebd8e 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -5,12 +5,12 @@ namespace Dapr.Actors.Generators.Helpers { /// - /// Syntax factory helpers for generating exception syntax. + /// Syntax factory helpers for generating syntax. /// public static partial class SyntaxFactoryHelpers { /// - /// Generates a syntax for syntax for the given argument name. + /// Generates a syntax for an based on the given argument name. /// /// Name of the argument that generated the exception. /// Returns used to throw an . @@ -80,7 +80,7 @@ public static ExpressionSyntax NameOfExpression(string argumentName) /// Name of remote method to invoke. /// Remote method parameters. /// Return types of remote method invocation. - /// The representing a call to the actor proxy. + /// Returns the representing a call to the actor proxy. public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync( MemberAccessExpressionSyntax actorProxyMemberSyntax, string remoteMethodName, @@ -122,11 +122,11 @@ public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync( } /// - /// Returns the syntax kinds for the specified accessibility. + /// Returns the for the specified accessibility. /// - /// Accessibility to convert into a SyntaxKind. - /// Return the collection of representing the given accessibility. - /// Throws when un unexpected syntax is passed. + /// 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(); diff --git a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs index b87be6631..e1f54fac4 100644 --- a/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs +++ b/src/Dapr.Actors.Generators/Models/ActorClientDescriptor.cs @@ -9,7 +9,7 @@ namespace Dapr.Actors.Generators.Models internal record class ActorClientDescriptor : IEquatable { /// - /// Gets the actor interface symbol. + /// Gets or sets the symbol representing the actor interface. /// public INamedTypeSymbol InterfaceType { get; set; } = null!; From 4e995864ee81f918e2068c6d13e1e7b57fd8e3f9 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Tue, 10 Sep 2024 22:59:33 +0200 Subject: [PATCH 62/63] Handled review requests Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/ActorClientGenerator.cs | 4 ++-- src/Dapr.Actors.Generators/Templates.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index c0e39fcdf..001604d53 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -60,7 +60,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) /// Current generator syntax context passed from generator pipeline. /// Cancellation token used to interrupt the generation. /// Returns the descriptor of actor client to generate. - static ActorClientDescriptor CreateActorClientDescriptor( + private static ActorClientDescriptor CreateActorClientDescriptor( GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { @@ -102,7 +102,7 @@ static ActorClientDescriptor CreateActorClientDescriptor( /// 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. - static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor descriptor) + private static void GenerateActorClientCode(SourceProductionContext context, ActorClientDescriptor descriptor) { try { diff --git a/src/Dapr.Actors.Generators/Templates.cs b/src/Dapr.Actors.Generators/Templates.cs index a9482a980..6cc4c9f87 100644 --- a/src/Dapr.Actors.Generators/Templates.cs +++ b/src/Dapr.Actors.Generators/Templates.cs @@ -18,7 +18,7 @@ internal static partial class Templates /// Throws when destinationNamespace is null. public static SourceText ActorMethodAttributeSourceText(string destinationNamespace) { - if (destinationNamespace == null) + if (destinationNamespace is null) { throw new ArgumentNullException(nameof(destinationNamespace)); } @@ -54,7 +54,7 @@ internal sealed class {Constants.ActorMethodAttributeTypeName} : Attribute /// Throws when destinationNamespace is null. public static SourceText GenerateActorClientAttributeSourceText(string destinationNamespace) { - if (destinationNamespace == null) + if (destinationNamespace is null) { throw new ArgumentNullException(nameof(destinationNamespace)); } From a50e6de3063cbc85649b56aa357d705888b96489 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 21 Oct 2024 22:56:07 +0200 Subject: [PATCH 63/63] Changed SyntaxFactoryHelpers accessor to internal Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs index 19d5ebd8e..36df7b280 100644 --- a/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs +++ b/src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs @@ -7,7 +7,7 @@ namespace Dapr.Actors.Generators.Helpers /// /// Syntax factory helpers for generating syntax. /// - public static partial class SyntaxFactoryHelpers + internal static partial class SyntaxFactoryHelpers { /// /// Generates a syntax for an based on the given argument name.