Skip to content

Commit

Permalink
Expose middleware properties in activity/orchestrator
Browse files Browse the repository at this point in the history
The properties bag from DispatchMiddlewareContext contains information
that might be useful from activities/orchestrators. However, they
currently do not show up from those context options. This change plumbs
the dictionary through and surfaces it for both activities and
orchestrators.

As part of this, a new interface IContextProperties is added that just
has the properties dictionary. However, this allows for shared code to
set and get properties by type or by name.
  • Loading branch information
twsouthwick committed Sep 8, 2023
1 parent 54436c6 commit b9b95fb
Show file tree
Hide file tree
Showing 15 changed files with 662 additions and 22 deletions.
147 changes: 147 additions & 0 deletions Test/DurableTask.Core.Tests/PropertiesMiddlewareTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// ----------------------------------------------------------------------------------
// Copyright Microsoft Corporation
// 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
namespace DurableTask.Core.Tests
{
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using DurableTask.Emulator;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class PropertiesMiddlewareTests
{
private const string PropertyKey = "Test";
private const string PropertyValue = "Value";

TaskHubWorker worker = null!;
TaskHubClient client = null!;

[TestInitialize]
public async Task Initialize()
{
var service = new LocalOrchestrationService();
this.worker = new TaskHubWorker(service);

await this.worker
.AddTaskOrchestrations(typeof(NoActivities), typeof(RunActivityOrchestrator))
.AddTaskActivities(typeof(ReturnPropertyActivity))
.StartAsync();

this.client = new TaskHubClient(service);
}

[TestCleanup]
public async Task TestCleanup()
{
await this.worker.StopAsync(true);
}

private sealed class NoActivities : TaskOrchestration<string, string>
{
public override Task<string> RunTask(OrchestrationContext context, string input)
{
return Task.FromResult(context.GetProperty<string>(PropertyKey)!);
}
}

private sealed class ReturnPropertyActivity : TaskActivity<string, string>
{
protected override string Execute(TaskContext context, string input)
{
return context.GetProperty<string>(PropertyKey)!;
}
}

private sealed class RunActivityOrchestrator : TaskOrchestration<string, string>
{
public override Task<string> RunTask(OrchestrationContext context, string input)
{
return context.ScheduleTask<string>(typeof(ReturnPropertyActivity));
}
}

[TestMethod]
public async Task OrchestrationGetsProperties()
{
this.worker.AddOrchestrationDispatcherMiddleware((context, next) =>
{
context.SetProperty<string>(PropertyKey, PropertyValue);
return next();
});

OrchestrationInstance instance = await this.client.CreateOrchestrationInstanceAsync(typeof(NoActivities), null);

TimeSpan timeout = TimeSpan.FromSeconds(Debugger.IsAttached ? 1000 : 10);
var state = await this.client.WaitForOrchestrationAsync(instance, timeout);

Assert.AreEqual($"\"{PropertyValue}\"", state.Output);
}

[TestMethod]
public async Task OrchestrationDoesNotGetPropertiesFromActivityMiddleware()
{
this.worker.AddActivityDispatcherMiddleware((context, next) =>
{
context.SetProperty<string>(PropertyKey, PropertyValue);
return next();
});

OrchestrationInstance instance = await this.client.CreateOrchestrationInstanceAsync(typeof(NoActivities), null);

TimeSpan timeout = TimeSpan.FromSeconds(Debugger.IsAttached ? 1000 : 10);
var state = await this.client.WaitForOrchestrationAsync(instance, timeout);

Assert.IsNull(state.Output);
}

[TestMethod]
public async Task ActivityGetsProperties()
{
this.worker.AddActivityDispatcherMiddleware((context, next) =>
{
context.SetProperty<string>(PropertyKey, PropertyValue);
return next();
});

OrchestrationInstance instance = await this.client.CreateOrchestrationInstanceAsync(typeof(RunActivityOrchestrator), null);

TimeSpan timeout = TimeSpan.FromSeconds(Debugger.IsAttached ? 1000 : 10);
var state = await this.client.WaitForOrchestrationAsync(instance, timeout);

Assert.AreEqual($"\"{PropertyValue}\"", state.Output);
}

[TestMethod]
public async Task ActivityDoesNotGetPropertiesFromOrchestratorMiddleware()
{
this.worker.AddOrchestrationDispatcherMiddleware((context, next) =>
{
context.SetProperty<string>(PropertyKey, PropertyValue);
return next();
});

OrchestrationInstance instance = await this.client.CreateOrchestrationInstanceAsync(typeof(RunActivityOrchestrator), null);

TimeSpan timeout = TimeSpan.FromSeconds(Debugger.IsAttached ? 1000 : 10);
var state = await this.client.WaitForOrchestrationAsync(instance, timeout);

Assert.IsNull(state.Output);
}
}
}
2 changes: 1 addition & 1 deletion Test/DurableTask.Core.Tests/RetryInterceptorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ sealed class MockOrchestrationContext : TaskOrchestrationContext
readonly List<TimeSpan> delays = new List<TimeSpan>();

public MockOrchestrationContext(OrchestrationInstance orchestrationInstance, TaskScheduler taskScheduler)
: base(orchestrationInstance, taskScheduler)
: base(orchestrationInstance, new PropertiesDictionary(), taskScheduler)
{
CurrentUtcDateTime = DateTime.UtcNow;
}
Expand Down
Loading

0 comments on commit b9b95fb

Please sign in to comment.