Skip to content

Commit

Permalink
Adds support for providing Actor type name during runtime. (#721)
Browse files Browse the repository at this point in the history
* Adds support for providing Actor type name during runtime.

- Fixes #677

Signed-off-by: Martin Björkström <martin.bjorkstrom@gmail.com>

* Make ActorTypeInformation.TryGet(Type) and ActorTypeInformation.Get(Type) obsolete.

- Fix relevant code after obsoleting.

Signed-off-by: Martin Björkström <martin.bjorkstrom@gmail.com>
  • Loading branch information
bjorkstromm authored Feb 15, 2022
1 parent 52113a3 commit dca6106
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 28 deletions.
33 changes: 29 additions & 4 deletions src/Dapr.Actors/Runtime/ActorHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,20 @@ public sealed class ActorHost
public static ActorHost CreateForTest<TActor>(ActorTestOptions options = null)
where TActor : Actor
{
return CreateForTest(typeof(TActor), options);
return CreateForTest(typeof(TActor), actorTypeName: null, options);
}

/// <summary>
/// Creates an instance of <see cref="ActorHost" /> for unit testing an actor instance.
/// </summary>
/// <param name="actorTypeName">The name of the actor type represented by the actor.</param>
/// <param name="options">The <see cref="ActorTestOptions" /> for configuring the host.</param>
/// <typeparam name="TActor">The actor type.</typeparam>
/// <returns>An <see cref="ActorHost" /> instance.</returns>
public static ActorHost CreateForTest<TActor>(string actorTypeName, ActorTestOptions options = null)
where TActor : Actor
{
return CreateForTest(typeof(TActor), actorTypeName, options);
}

/// <summary>
Expand All @@ -42,17 +55,29 @@ public static ActorHost CreateForTest<TActor>(ActorTestOptions options = null)
/// <param name="options">The <see cref="ActorTestOptions" /> for configuring the host.</param>
/// <returns>An <see cref="ActorHost" /> instance.</returns>
public static ActorHost CreateForTest(Type actorType, ActorTestOptions options = null)
{
return CreateForTest(actorType, actorTypeName: null, options);
}

/// <summary>
/// Creates an instance of <see cref="ActorHost" /> for unit testing an actor instance.
/// </summary>
/// <param name="actorType">The actor type.</param>
/// <param name="actorTypeName">The name of the actor type represented by the actor.</param>
/// <param name="options">The <see cref="ActorTestOptions" /> for configuring the host.</param>
/// <returns>An <see cref="ActorHost" /> instance.</returns>
public static ActorHost CreateForTest(Type actorType, string actorTypeName, ActorTestOptions options = null)
{
if (actorType == null)
{
throw new ArgumentNullException(nameof(actorType));
}

options ??= new ActorTestOptions();

return new ActorHost(
ActorTypeInformation.Get(actorType),
options.ActorId,
ActorTypeInformation.Get(actorType, actorTypeName),
options.ActorId,
options.JsonSerializerOptions,
options.LoggerFactory,
options.ProxyFactory,
Expand Down
15 changes: 14 additions & 1 deletion src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,20 @@ protected override ActorTypeInformation GetKeyForItem(ActorRegistration item)
public void RegisterActor<TActor>(Action<ActorRegistration> configure = null)
where TActor : Actor
{
var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor));
RegisterActor<TActor>(actorTypeName: null, configure);
}

/// <summary>
/// Registers an actor type in the collection.
/// </summary>
/// <typeparam name="TActor">Type of actor.</typeparam>
/// <param name="actorTypeName">The name of the actor type represented by the actor.</param>
/// <param name="configure">An optional delegate used to configure the actor registration.</param>
/// <remarks>The value of <paramref name="actorTypeName"/> will have precedence over the default actor type name derived from the actor implementation type or any type name set via <see cref="ActorAttribute"/>.</remarks>
public void RegisterActor<TActor>(string actorTypeName, Action<ActorRegistration> configure = null)
where TActor : Actor
{
var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor), actorTypeName);
var registration = new ActorRegistration(actorTypeInfo);
configure?.Invoke(registration);
this.Add(registration);
Expand Down
23 changes: 21 additions & 2 deletions src/Dapr.Actors/Runtime/ActorTypeInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ private ActorTypeInformation()
/// <para>1. <see cref="System.Type.BaseType"/> for actorType is not of type <see cref="Actor"/>.</para>
/// <para>2. actorType does not implement an interface deriving from <see cref="IActor"/> and is not marked as abstract.</para>
/// </remarks>
[Obsolete("Use Get(Type, string) instead. This will be removed in the future.")]
public static bool TryGet(Type actorType, out ActorTypeInformation actorTypeInformation)
{
try
{
actorTypeInformation = Get(actorType);
actorTypeInformation = Get(actorType, actorTypeName: null);
return true;
}
catch (ArgumentException)
Expand All @@ -99,7 +100,25 @@ public static bool TryGet(Type actorType, out ActorTypeInformation actorTypeInfo
/// <para>When actorType does not implement an interface deriving from <see cref="IActor"/>
/// and is not marked as abstract.</para>
/// </exception>
[Obsolete("Use Get(Type, string) instead. This will be removed in the future.")]
public static ActorTypeInformation Get(Type actorType)
{
return Get(actorType, actorTypeName: null);
}

/// <summary>
/// Creates an <see cref="ActorTypeInformation"/> from actorType.
/// </summary>
/// <param name="actorType">The type of class implementing the actor to create ActorTypeInformation for.</param>
/// <param name="actorTypeName">The name of the actor type represented by the actor.</param>
/// <returns><see cref="ActorTypeInformation"/> created from actorType.</returns>
/// <exception cref="System.ArgumentException">
/// <para>When <see cref="System.Type.BaseType"/> for actorType is not of type <see cref="Actor"/>.</para>
/// <para>When actorType does not implement an interface deriving from <see cref="IActor"/>
/// and is not marked as abstract.</para>
/// </exception>
/// <remarks>The value of <paramref name="actorTypeName"/> will have precedence over the default actor type name derived from the actor implementation type or any type name set via <see cref="ActorAttribute"/>.</remarks>
public static ActorTypeInformation Get(Type actorType, string actorTypeName)
{
if (!actorType.IsActor())
{
Expand Down Expand Up @@ -129,7 +148,7 @@ public static ActorTypeInformation Get(Type actorType)

var actorAttribute = actorType.GetCustomAttribute<ActorAttribute>();

string actorTypeName = actorAttribute?.TypeName ?? actorType.Name;
actorTypeName ??= actorAttribute?.TypeName ?? actorType.Name;

return new ActorTypeInformation()
{
Expand Down
32 changes: 16 additions & 16 deletions test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

using System.Linq;
using System.Text.Json;
Expand Down Expand Up @@ -38,8 +38,8 @@ public void CanRegisterActorsInSingleCalls()

Assert.Collection(
runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t),
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1)).ActorTypeName, t),
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2)).ActorTypeName, t));
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t),
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t));
}

[Fact]
Expand All @@ -62,8 +62,8 @@ public void CanRegisterActorsInMultipleCalls()

Assert.Collection(
runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t),
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1)).ActorTypeName, t),
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2)).ActorTypeName, t));
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t),
t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t));
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ private IServiceProvider CreateServices()

private DependencyInjectionActorActivator CreateActivator(Type type)
{
return new DependencyInjectionActorActivator(CreateServices(), ActorTypeInformation.Get(type));
return new DependencyInjectionActorActivator(CreateServices(), ActorTypeInformation.Get(type, actorTypeName: null));
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public sealed class ActorManagerTests
{
private ActorManager CreateActorManager(Type type, ActorActivator activator = null)
{
var registration = new ActorRegistration(ActorTypeInformation.Get(type));
var registration = new ActorRegistration(ActorTypeInformation.Get(type, actorTypeName: null));
var interactor = new DaprHttpInteractor(clientHandler: null, "http://localhost:3500", apiToken: null, requestTimeout: null);
return new ActorManager(registration, activator ?? new DefaultActorActivator(), JsonSerializerDefaults.Web, NullLoggerFactory.Instance, ActorProxy.DefaultProxyFactory, interactor);
}
Expand Down
2 changes: 1 addition & 1 deletion test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public sealed class ActorRuntimeOptionsTests
public void TestRegisterActor_SavesActivator()
{
var actorType = typeof(TestActor);
var actorTypeInformation = ActorTypeInformation.Get(actorType);
var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null);
var host = ActorHost.CreateForTest<TestActor>();
var actor = new TestActor(host);

Expand Down
27 changes: 27 additions & 0 deletions test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ namespace Dapr.Actors.Test
using Microsoft.Extensions.Logging;
using Xunit;
using Dapr.Actors.Client;
using System.Reflection;

public sealed class ActorRuntimeTests
{
private const string RenamedActorTypeName = "MyRenamedActor";
private const string ParamActorTypeName = "AnotherRenamedActor";
private readonly ILoggerFactory loggerFactory = new LoggerFactory();
private readonly ActorActivatorFactory activatorFactory = new DefaultActorActivatorFactory();

Expand Down Expand Up @@ -63,6 +65,31 @@ public void TestExplicitActorType()
Assert.Contains(RenamedActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture);
}

[Fact]
public void TestExplicitActorTypeAsParamShouldOverrideInferred()
{
var actorType = typeof(TestActor);
var options = new ActorRuntimeOptions();
options.Actors.RegisterActor<TestActor>(ParamActorTypeName);
var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory);

Assert.NotEqual(ParamActorTypeName, actorType.Name);
Assert.Contains(ParamActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture);
}

[Fact]
public void TestExplicitActorTypeAsParamShouldOverrideActorAttribute()
{
var actorType = typeof(RenamedActor);
var options = new ActorRuntimeOptions();
options.Actors.RegisterActor<RenamedActor>(ParamActorTypeName);
var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory);

Assert.NotEqual(ParamActorTypeName, actorType.Name);
Assert.NotEqual(ParamActorTypeName, actorType.GetCustomAttribute<ActorAttribute>().TypeName);
Assert.Contains(ParamActorTypeName, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture);
}

// This tests the change that removed the Activate message from Dapr runtime -> app.
[Fact]
public async Task NoActivateMessageFromRuntime()
Expand Down
20 changes: 18 additions & 2 deletions test/Dapr.Actors.Test/Runtime/ActorTypeInformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@

namespace Dapr.Actors.Test
{
using System;
using System.Reflection;
using Dapr.Actors;
using Dapr.Actors.Runtime;
using Xunit;

public sealed class ActorTypeInformationTests
{
private const string RenamedActorTypeName = "MyRenamedActor";
private const string ParamActorTypeName = "AnotherRenamedActor";

private interface ITestActor : IActor
{
Expand All @@ -29,7 +32,7 @@ private interface ITestActor : IActor
public void TestInferredActorType()
{
var actorType = typeof(TestActor);
var actorTypeInformation = ActorTypeInformation.Get(actorType);
var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null);

Assert.Equal(actorType.Name, actorTypeInformation.ActorTypeName);
}
Expand All @@ -41,11 +44,24 @@ public void TestExplicitActorType()

Assert.NotEqual(RenamedActorTypeName, actorType.Name);

var actorTypeInformation = ActorTypeInformation.Get(actorType);
var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null);

Assert.Equal(RenamedActorTypeName, actorTypeInformation.ActorTypeName);
}

[Theory]
[InlineData(typeof(TestActor))]
[InlineData(typeof(RenamedActor))]
public void TestExplicitActorTypeAsParam(Type actorType)
{
Assert.NotEqual(ParamActorTypeName, actorType.Name);
Assert.NotEqual(ParamActorTypeName, actorType.GetCustomAttribute<ActorAttribute>()?.TypeName);

var actorTypeInformation = ActorTypeInformation.Get(actorType, ParamActorTypeName);

Assert.Equal(ParamActorTypeName, actorTypeInformation.ActorTypeName);
}

private sealed class TestActor : Actor, ITestActor
{
public TestActor(ActorHost host)
Expand Down

0 comments on commit dca6106

Please sign in to comment.