Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Abstractions/TaskOrchestrationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public abstract class TaskOrchestrationContext
/// </value>
public abstract bool IsReplaying { get; }

/// <summary>
/// Gets the version of the current orchestration instance, which was set when the instance was created.
/// </summary>
public abstract string Version { get; }

/// <summary>
/// Gets the entity feature, for interacting with entities.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,16 @@ public static IDurableTaskClientBuilder UseBuildTarget<TTarget, TOptions>(this I
});
return builder;
}

/// <summary>
/// Sets the default version for this builder. This version will be applied by default to all orchestrations if set.
/// </summary>
/// <param name="builder">The builder to set the version for.</param>
/// <param name="version">The version that will be used as the default version.</param>
/// <returns>The original builder, for call chaining.</returns>
public static IDurableTaskClientBuilder UseDefaultVersion(this IDurableTaskClientBuilder builder, string version)
{
builder.Configure(options => options.DefaultVersion = version);
return builder;
}
}
13 changes: 13 additions & 0 deletions src/Client/Core/DurableTaskClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ public class DurableTaskClientOptions
DataConverter dataConverter = JsonDataConverter.Default;
bool enableEntitySupport;

/// <summary>
/// Gets or sets the version of orchestrations that will be created.
/// </summary>
/// <remarks>
/// Currently, this is sourced from the AzureManaged client options.
/// </remarks>
public string DefaultVersion { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the data converter. Default value is <see cref="JsonDataConverter.Default" />.
/// </summary>
Expand Down Expand Up @@ -95,6 +103,11 @@ internal void ApplyTo(DurableTaskClientOptions other)
{
other.EnableEntitySupport = this.EnableEntitySupport;
}

if (!string.IsNullOrWhiteSpace(this.DefaultVersion))
{
other.DefaultVersion = this.DefaultVersion;
}
}
}
}
12 changes: 11 additions & 1 deletion src/Client/Grpc/GrpcDurableTaskClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,20 @@ public override async Task<string> ScheduleNewOrchestrationInstanceAsync(
{
Check.NotEntity(this.options.EnableEntitySupport, options?.InstanceId);

string version = string.Empty;
if (!string.IsNullOrEmpty(orchestratorName.Version))
{
version = orchestratorName.Version;
}
else if (!string.IsNullOrEmpty(this.options.DefaultVersion))
{
version = this.options.DefaultVersion;
}

var request = new P.CreateInstanceRequest
{
Name = orchestratorName.Name,
Version = orchestratorName.Version,
Version = version,
InstanceId = options?.InstanceId ?? Guid.NewGuid().ToString("N"),
Input = this.DataConverter.Serialize(input),
};
Expand Down
3 changes: 3 additions & 0 deletions src/Worker/Core/Shims/TaskOrchestrationContextWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public override TaskOrchestrationEntityFeature Entities
}
}

/// <inheritdoc/>
public override string Version => this.innerContext.Version;

/// <summary>
/// Gets the DataConverter to use for inputs, outputs, and entity states.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ public void UseGrpc_Callback_Sets()
options.Address.Should().BeNull();
}

[Fact]
public void UseDefaultVersion_DefaultVersion_Sets()
{
ServiceCollection services = new();
DefaultDurableTaskClientBuilder builder = new(null, services);
builder.UseDefaultVersion("0.1")
.UseGrpc();

IServiceProvider provider = services.BuildServiceProvider();
GrpcDurableTaskClientOptions options = provider.GetOptions<GrpcDurableTaskClientOptions>();

options.DefaultVersion.Should().Be("0.1");
}

#if NET6_0_OR_GREATER
static GrpcChannel GetChannel() => GrpcChannel.ForAddress("http://localhost:9001");
#endif
Expand Down
16 changes: 9 additions & 7 deletions test/Grpc.IntegrationTests/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// Licensed under the MIT License.

using System.Diagnostics;
using Microsoft.DurableTask.Tests.Logging;
using Microsoft.DurableTask.Client;
using Microsoft.DurableTask.Tests.Logging;
using Microsoft.DurableTask.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.DurableTask.Grpc.Tests;

Expand Down Expand Up @@ -43,18 +43,19 @@ void IDisposable.Dispose()
GC.SuppressFinalize(this);
}

protected async Task<HostTestLifetime> StartWorkerAsync(Action<IDurableTaskWorkerBuilder> configure)
protected async Task<HostTestLifetime> StartWorkerAsync(Action<IDurableTaskWorkerBuilder> workerConfigure, Action<IDurableTaskClientBuilder>? clientConfigure = null)
{
IHost host = this.CreateHostBuilder(configure).Build();
IHost host = this.CreateHostBuilder(workerConfigure, clientConfigure).Build();
await host.StartAsync(this.TimeoutToken);
return new HostTestLifetime(host, this.TimeoutToken);
}

/// <summary>
/// Creates a <see cref="IHostBuilder"/> configured to output logs to xunit logging infrastructure.
/// </summary>
/// <param name="configure">Configures the durable task builder.</param>
protected IHostBuilder CreateHostBuilder(Action<IDurableTaskWorkerBuilder> configure)
/// <param name="workerConfigure">Configures the durable task worker builder.</param>
/// <param name="clientConfigure">Configures the durable task client builder.</param>
protected IHostBuilder CreateHostBuilder(Action<IDurableTaskWorkerBuilder> workerConfigure, Action<IDurableTaskClientBuilder>? clientConfigure)
{
return Host.CreateDefaultBuilder()
.ConfigureLogging(b =>
Expand All @@ -68,13 +69,14 @@ protected IHostBuilder CreateHostBuilder(Action<IDurableTaskWorkerBuilder> confi
services.AddDurableTaskWorker(b =>
{
b.UseGrpc(this.sidecarFixture.Channel);
configure(b);
workerConfigure(b);
});

services.AddDurableTaskClient(b =>
{
b.UseGrpc(this.sidecarFixture.Channel);
b.RegisterDirectly();
clientConfigure?.Invoke(b);
});
});
}
Expand Down
35 changes: 32 additions & 3 deletions test/Grpc.IntegrationTests/OrchestrationPatterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.DurableTask.Worker;
using Microsoft.DurableTask.Client;
using Microsoft.DurableTask.Tests.Logging;
using Microsoft.DurableTask.Worker;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
using Microsoft.DurableTask.Client;

namespace Microsoft.DurableTask.Grpc.Tests;

Expand Down Expand Up @@ -561,8 +561,37 @@ public async Task SpecialSerialization()
Assert.Equal("new value", output?["newProperty"]?.ToString());
}

// TODO: Additional versioning tests
[Fact]
public async Task OrchestrationVersionPassedThroughContext()
{
var version = "0.1";
await using HostTestLifetime server = await this.StartWorkerAsync(b =>
{
b.AddTasks(tasks => tasks
.AddOrchestratorFunc<string, string>("Versioned_Orchestration", (ctx, input) =>
{
return ctx.CallActivityAsync<string>("Versioned_Activity", ctx.Version);
})
.AddActivityFunc<string, string>("Versioned_Activity", (ctx, input) =>
{
return $"Orchestration version: {input}";
}));
}, c =>
{
c.UseDefaultVersion(version);
});

var instanceId = await server.Client.ScheduleNewOrchestrationInstanceAsync("Versioned_Orchestration", input: string.Empty);
var result = await server.Client.WaitForInstanceCompletionAsync(instanceId, getInputsAndOutputs: true, this.TimeoutToken);
var output = result.ReadOutputAs<string>();

Assert.NotNull(output);
Assert.Equal(output, $"Orchestration version: {version}");

}

// TODO: Test for multiple external events with the same name
// TODO: Test for ContinueAsNew with external events that carry over
// TODO: Test for catching activity exceptions of specific types
// TODO: Versioning tests
}
Loading