From fe86f77744b5586d2f91cc945239748d7325f82d Mon Sep 17 00:00:00 2001 From: tgrieger Date: Mon, 25 Nov 2024 08:48:36 -0500 Subject: [PATCH] Store `MethodInfo` when creating an `ActivityDefinition` (#369) --- .../Activities/ActivityDefinition.cs | 73 ++++++++++++------- .../Activities/ActivityDefinitionTests.cs | 35 +++++++++ 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/src/Temporalio/Activities/ActivityDefinition.cs b/src/Temporalio/Activities/ActivityDefinition.cs index a52cfaea..6d97ce24 100644 --- a/src/Temporalio/Activities/ActivityDefinition.cs +++ b/src/Temporalio/Activities/ActivityDefinition.cs @@ -19,13 +19,15 @@ private ActivityDefinition( Type returnType, IReadOnlyCollection parameterTypes, int requiredParameterCount, - Func invoker) + Func invoker, + MethodInfo? methodInfo) { Name = name; ReturnType = returnType; ParameterTypes = parameterTypes; RequiredParameterCount = requiredParameterCount; this.invoker = invoker; + MethodInfo = methodInfo; } /// @@ -56,6 +58,11 @@ private ActivityDefinition( /// public bool Dynamic => Name == null; + /// + /// Gets the . Will only have a value if one was used to create this . + /// + public MethodInfo? MethodInfo { get; private init; } + /// /// Create an activity definition from a delegate. is /// called on this delegate. The delegate must have an associated method and that method @@ -89,31 +96,7 @@ public static ActivityDefinition Create( int requiredParameterCount, Func invoker) { - // If there is a null name, which means dynamic, there must only be one parameter type - // and it must be varargs IRawValue - if (name == null && ( - requiredParameterCount != 1 || - parameterTypes.SingleOrDefault() != typeof(Converters.IRawValue[]))) - { - throw new ArgumentException( - $"Dynamic activity must accept a required array of IRawValue"); - } - - if (requiredParameterCount > parameterTypes.Count) - { - throw new ArgumentException( - $"Activity {name} has more required parameters than parameters", - nameof(requiredParameterCount)); - } - foreach (var parameterType in parameterTypes) - { - if (parameterType.IsByRef) - { - throw new ArgumentException( - $"Activity {name} has disallowed ref/out parameter"); - } - } - return new(name, returnType, parameterTypes, requiredParameterCount, invoker); + return Create(name, returnType, parameterTypes, requiredParameterCount, invoker, methodInfo: null); } /// @@ -136,7 +119,8 @@ public static ActivityDefinition Create(MethodInfo method, Func p.ParameterType).ToArray(), parms.Count(p => !p.HasDefaultValue), - parameters => invoker.Invoke(ParametersWithDefaults(parms, parameters))); + parameters => invoker.Invoke(ParametersWithDefaults(parms, parameters)), + method); } /// @@ -300,5 +284,40 @@ internal static string NameFromMethodForCall(MethodInfo method) } return name; } + + private static ActivityDefinition Create( + string? name, + Type returnType, + IReadOnlyCollection parameterTypes, + int requiredParameterCount, + Func invoker, + MethodInfo? methodInfo) + { + // If there is a null name, which means dynamic, there must only be one parameter type + // and it must be varargs IRawValue + if (name == null && ( + requiredParameterCount != 1 || + parameterTypes.SingleOrDefault() != typeof(Converters.IRawValue[]))) + { + throw new ArgumentException( + $"Dynamic activity must accept a required array of IRawValue"); + } + + if (requiredParameterCount > parameterTypes.Count) + { + throw new ArgumentException( + $"Activity {name} has more required parameters than parameters", + nameof(requiredParameterCount)); + } + foreach (var parameterType in parameterTypes) + { + if (parameterType.IsByRef) + { + throw new ArgumentException( + $"Activity {name} has disallowed ref/out parameter"); + } + } + return new(name, returnType, parameterTypes, requiredParameterCount, invoker, methodInfo); + } } } \ No newline at end of file diff --git a/tests/Temporalio.Tests/Activities/ActivityDefinitionTests.cs b/tests/Temporalio.Tests/Activities/ActivityDefinitionTests.cs index 5e764ecc..fd950002 100644 --- a/tests/Temporalio.Tests/Activities/ActivityDefinitionTests.cs +++ b/tests/Temporalio.Tests/Activities/ActivityDefinitionTests.cs @@ -1,5 +1,6 @@ namespace Temporalio.Tests.Activities; +using System.Reflection; using System.Threading.Tasks; using Temporalio.Activities; using Temporalio.Converters; @@ -173,6 +174,40 @@ public async Task CreateAll_ClosedGeneric_CanInvoke() Assert.Equal("some-val", await defn.InvokeAsync(Array.Empty())); } + [Fact] + public async Task Create_WithMethodInfo_HasValidMethodInfo() + { + var methodInfo = typeof(ActivityDefinitionTests) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) + .Single(mi => mi.Name.Equals(nameof(GoodAct1Async), StringComparison.Ordinal)); + + var defn = ActivityDefinition.Create(methodInfo, objects => methodInfo!.Invoke(this, objects)); + Assert.Equal(methodInfo, defn.MethodInfo); + } + + [Fact] + public async Task Create_WithDelegate_HasValidMethodInfo() + { + var methodInfo = typeof(ActivityDefinitionTests) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) + .Single(mi => mi.Name.Equals(nameof(GoodAct1Async), StringComparison.Ordinal)); + + var defn = ActivityDefinition.Create(GoodAct1Async); + Assert.Equal(methodInfo, defn.MethodInfo); + } + + [Fact] + public async Task Create_WithLambda_DoesNotHaveValidMethodInfo() + { + var defn = ActivityDefinition.Create( + "some-name", + typeof(int), + new Type[] { typeof(int) }, + 1, + parameters => ((int)parameters[0]!) + 5); + Assert.Null(defn.MethodInfo); + } + protected static void BadAct1() { }