Skip to content

Commit 215bd1b

Browse files
committed
Add utilities for easier versioning usage
Signed-off-by: halspang <[email protected]>
1 parent 8278676 commit 215bd1b

File tree

3 files changed

+245
-0
lines changed

3 files changed

+245
-0
lines changed

src/Abstractions/TaskOrchestrationContext.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,115 @@ public virtual ILogger CreateReplaySafeLogger(Type type)
403403
public virtual ILogger CreateReplaySafeLogger<T>()
404404
=> new ReplaySafeLogger(this, this.LoggerFactory.CreateLogger<T>());
405405

406+
/// <summary>
407+
/// Checks if the current orchestration version is greater than the specified version.
408+
/// </summary>
409+
/// <remarks>
410+
/// <para>
411+
/// If both versions are empty, this is considered false as neither can be greater.
412+
/// </para>
413+
/// <para>
414+
/// An empty context version is less than a defined version in the parameter.
415+
/// </para>
416+
/// <para>
417+
/// An empty parameter version is less than a defined version in the context.
418+
/// </para>
419+
/// </remarks>
420+
/// <param name="version">The version to check against.</param>
421+
/// <returns>True if the orchestration's version is greater than the provided version, false otherwise.</returns>
422+
public virtual bool IsVersionGreaterThan(string version)
423+
{
424+
if (string.IsNullOrWhiteSpace(this.Version) && string.IsNullOrWhiteSpace(version))
425+
{
426+
return false;
427+
}
428+
429+
// An empty version in the context is always less than a defined version in the parameter.
430+
if (string.IsNullOrWhiteSpace(this.Version))
431+
{
432+
return false;
433+
}
434+
435+
// An empty version in the parameter is always less than a defined version in the context.
436+
if (string.IsNullOrWhiteSpace(version))
437+
{
438+
return true;
439+
}
440+
441+
// We check this Version complies with the standard versioning format on set, so this is safe.
442+
Version contextVersion = new Version(this.Version);
443+
try
444+
{
445+
Version parameterVersion = new Version(version);
446+
return contextVersion > parameterVersion;
447+
}
448+
catch (Exception) // This can be several different exception types.
449+
{
450+
// The version string is not in a valid format.
451+
return false;
452+
}
453+
}
454+
455+
/// <summary>
456+
/// Checks if the current orchestration version is less than the specified version.
457+
/// </summary>
458+
/// <remarks>
459+
/// <para>
460+
/// If both versions are empty, this is considered false as neither can be greater.
461+
/// </para>
462+
/// <para>
463+
/// An empty context version is less than a defined version in the parameter.
464+
/// </para>
465+
/// <para>
466+
/// An empty parameter version is less than a defined version in the context.
467+
/// </para>
468+
/// </remarks>
469+
/// <param name="version">The version to check against.</param>
470+
/// <returns>True if the orchestration's version is less than the provided version, false otherwise.</returns>
471+
public virtual bool IsVersionLessThan(string version)
472+
{
473+
if (string.IsNullOrWhiteSpace(this.Version) && string.IsNullOrWhiteSpace(version))
474+
{
475+
return false;
476+
}
477+
478+
// An empty version in the context is always less than a defined version in the parameter.
479+
if (string.IsNullOrWhiteSpace(this.Version))
480+
{
481+
return true;
482+
}
483+
484+
// An empty version in the parameter is always less than a defined version in the context.
485+
if (string.IsNullOrWhiteSpace(version))
486+
{
487+
return false;
488+
}
489+
490+
// We check this Version complies with the standard versioning format on set, so this is safe.
491+
Version contextVersion = new Version(this.Version);
492+
try
493+
{
494+
Version parameterVersion = new Version(version);
495+
return contextVersion < parameterVersion;
496+
}
497+
catch (Exception) // This can be several different exception types.
498+
{
499+
// The version string is not in a valid format.
500+
return false;
501+
}
502+
}
503+
504+
/// <summary>
505+
/// Checks if the current orchestration version is equal to the specified version.
506+
/// </summary>
507+
/// <param name="version">The version to check against.</param>
508+
/// <returns>True if the versions are equal, false otherwise.</returns>
509+
public virtual bool IsVersionEqual(string version)
510+
{
511+
// A simple string comparison is fine here.
512+
return this.Version == version;
513+
}
514+
406515
class ReplaySafeLogger : ILogger
407516
{
408517
readonly TaskOrchestrationContext context;

src/Client/Core/DependencyInjection/DurableTaskClientBuilderExtensions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ public static IDurableTaskClientBuilder UseBuildTarget<TTarget, TOptions>(this I
9898
/// <returns>The original builder, for call chaining.</returns>
9999
public static IDurableTaskClientBuilder UseDefaultVersion(this IDurableTaskClientBuilder builder, string version)
100100
{
101+
// If the version is defined, make sure it conforms to the versioning scheme.
102+
if (!string.IsNullOrWhiteSpace(version))
103+
{
104+
try
105+
{
106+
// This will throw if the version is not valid.
107+
_ = new Version(version);
108+
}
109+
catch (Exception ex) // There are several exceptions that can be thrown, simplify to an ArgumentException.
110+
{
111+
throw new ArgumentException($"The provided version '{version}' does not conform to the versioning scheme.", nameof(version), ex);
112+
}
113+
}
114+
101115
builder.Configure(options => options.DefaultVersion = version);
102116
return builder;
103117
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace Microsoft.DurableTask.Abstractions.Tests;
7+
public class TaskOrchestrationContextVersionTests
8+
{
9+
[Theory]
10+
[InlineData("1.1", "1.0", true)]
11+
[InlineData("1.0", "1.1", false)]
12+
[InlineData("1.0", "1.0", false)]
13+
[InlineData("", "1.0", false)]
14+
[InlineData("1.0", "", true)]
15+
[InlineData("", "", false)]
16+
[InlineData("1.0.1", "1.0.0", true)]
17+
[InlineData("1.0.0", "1.0.1", false)]
18+
public void OrchestrationContext_Version_GreaterThanTests(string orchestrationVersion, string otherVersion, bool isGreater)
19+
{
20+
TaskOrchestrationContext orchestrationContext = new TestTaskOrchestrationContext(orchestrationVersion);
21+
22+
orchestrationContext.IsVersionGreaterThan(otherVersion).Should().Be(isGreater);
23+
}
24+
25+
[Theory]
26+
[InlineData("1.1", "1.0", false)]
27+
[InlineData("1.0", "1.1", true)]
28+
[InlineData("1.0", "1.0", false)]
29+
[InlineData("", "1.0", true)]
30+
[InlineData("1.0", "", false)]
31+
[InlineData("", "", false)]
32+
[InlineData("1.0.1", "1.0.0", false)]
33+
[InlineData("1.0.0", "1.0.1", true)]
34+
public void OrchestrationContext_Version_LessThanTests(string orchestrationVersion, string otherVersion, bool isLesser)
35+
{
36+
TaskOrchestrationContext orchestrationContext = new TestTaskOrchestrationContext(orchestrationVersion);
37+
38+
orchestrationContext.IsVersionLessThan(otherVersion).Should().Be(isLesser);
39+
}
40+
41+
[Theory]
42+
[InlineData("1.1", "1.0", false)]
43+
[InlineData("1.0", "1.1", false)]
44+
[InlineData("1.0", "1.0", true)]
45+
[InlineData("", "1.0", false)]
46+
[InlineData("1.0", "", false)]
47+
[InlineData("", "", true)]
48+
public void OrchestrationContext_Version_EqualTo(string orchestrationVersion, string otherVersion, bool isEqual)
49+
{
50+
TaskOrchestrationContext orchestrationContext = new TestTaskOrchestrationContext(orchestrationVersion);
51+
52+
orchestrationContext.IsVersionEqual(otherVersion).Should().Be(isEqual);
53+
}
54+
55+
class TestTaskOrchestrationContext : TaskOrchestrationContext
56+
{
57+
internal readonly string version = string.Empty;
58+
59+
public TestTaskOrchestrationContext(string version)
60+
{
61+
this.version = version;
62+
}
63+
public override TaskName Name => throw new NotImplementedException();
64+
65+
public override string InstanceId => throw new NotImplementedException();
66+
67+
public override ParentOrchestrationInstance? Parent => throw new NotImplementedException();
68+
69+
public override DateTime CurrentUtcDateTime => throw new NotImplementedException();
70+
71+
public override bool IsReplaying => throw new NotImplementedException();
72+
73+
public override string Version => this.version;
74+
75+
protected override ILoggerFactory LoggerFactory => throw new NotImplementedException();
76+
77+
public override Task<TResult> CallActivityAsync<TResult>(TaskName name, object? input = null, TaskOptions? options = null)
78+
{
79+
throw new NotImplementedException();
80+
}
81+
82+
public override Task<TResult> CallSubOrchestratorAsync<TResult>(TaskName orchestratorName, object? input = null, TaskOptions? options = null)
83+
{
84+
throw new NotImplementedException();
85+
}
86+
87+
public override void ContinueAsNew(object? newInput = null, bool preserveUnprocessedEvents = true)
88+
{
89+
throw new NotImplementedException();
90+
}
91+
92+
public override Task CreateTimer(DateTime fireAt, CancellationToken cancellationToken)
93+
{
94+
throw new NotImplementedException();
95+
}
96+
97+
public override T? GetInput<T>() where T : default
98+
{
99+
throw new NotImplementedException();
100+
}
101+
102+
public override Guid NewGuid()
103+
{
104+
throw new NotImplementedException();
105+
}
106+
107+
public override void SendEvent(string instanceId, string eventName, object payload)
108+
{
109+
throw new NotImplementedException();
110+
}
111+
112+
public override void SetCustomStatus(object? customStatus)
113+
{
114+
throw new NotImplementedException();
115+
}
116+
117+
public override Task<T> WaitForExternalEvent<T>(string eventName, CancellationToken cancellationToken = default)
118+
{
119+
throw new NotImplementedException();
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)