Skip to content

Commit

Permalink
Store MethodInfo when creating an ActivityDefinition (#369)
Browse files Browse the repository at this point in the history
  • Loading branch information
tgrieger authored Nov 25, 2024
1 parent 2400644 commit fe86f77
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 27 deletions.
73 changes: 46 additions & 27 deletions src/Temporalio/Activities/ActivityDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ private ActivityDefinition(
Type returnType,
IReadOnlyCollection<Type> parameterTypes,
int requiredParameterCount,
Func<object?[], object?> invoker)
Func<object?[], object?> invoker,
MethodInfo? methodInfo)
{
Name = name;
ReturnType = returnType;
ParameterTypes = parameterTypes;
RequiredParameterCount = requiredParameterCount;
this.invoker = invoker;
MethodInfo = methodInfo;
}

/// <summary>
Expand Down Expand Up @@ -56,6 +58,11 @@ private ActivityDefinition(
/// </summary>
public bool Dynamic => Name == null;

/// <summary>
/// Gets the <see cref="MethodInfo"/>. Will only have a value if one was used to create this <see cref="ActivityDefinition"/>.
/// </summary>
public MethodInfo? MethodInfo { get; private init; }

/// <summary>
/// Create an activity definition from a delegate. <see cref="Delegate.DynamicInvoke" /> is
/// called on this delegate. The delegate must have an associated method and that method
Expand Down Expand Up @@ -89,31 +96,7 @@ public static ActivityDefinition Create(
int requiredParameterCount,
Func<object?[], object?> 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);
}

/// <summary>
Expand All @@ -136,7 +119,8 @@ public static ActivityDefinition Create(MethodInfo method, Func<object?[], objec
method.ReturnType,
parms.Select(p => p.ParameterType).ToArray(),
parms.Count(p => !p.HasDefaultValue),
parameters => invoker.Invoke(ParametersWithDefaults(parms, parameters)));
parameters => invoker.Invoke(ParametersWithDefaults(parms, parameters)),
method);
}

/// <summary>
Expand Down Expand Up @@ -300,5 +284,40 @@ internal static string NameFromMethodForCall(MethodInfo method)
}
return name;
}

private static ActivityDefinition Create(
string? name,
Type returnType,
IReadOnlyCollection<Type> parameterTypes,
int requiredParameterCount,
Func<object?[], object?> 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);
}
}
}
35 changes: 35 additions & 0 deletions tests/Temporalio.Tests/Activities/ActivityDefinitionTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Temporalio.Tests.Activities;

using System.Reflection;
using System.Threading.Tasks;
using Temporalio.Activities;
using Temporalio.Converters;
Expand Down Expand Up @@ -173,6 +174,40 @@ public async Task CreateAll_ClosedGeneric_CanInvoke()
Assert.Equal("some-val", await defn.InvokeAsync(Array.Empty<object?>()));
}

[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()
{
}
Expand Down

0 comments on commit fe86f77

Please sign in to comment.