diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4098dffd..70369d261 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
Represents the **NuGet** versions.
+## v5.3.1
+- *Fixed:* Upgraded `CoreEx` and `UnitTestEx` to latest packages to include all related fixes.
+
## v5.3.0
- *Enhancement:* Added new code-generation configuration property `ValidationFramework` that supports either `CoreEx` (default) or `FluentValidation` (uses the `CoreEx.FluentValidation` interop wrapping capabilities) to allow entity validation to be performed using either framework. Supports mix-and-matching where required. The `CoreEx.Validation` framework is still leveraged for `IsMandatory` and `ValidatorCode` logic where specified.
- *[Issue 208](https://github.com/Avanade/Beef/issues/208): `ReferenceDataController.GetNamed` now excluded from Swagger output as results in superfluous types/models.
diff --git a/Common.targets b/Common.targets
index 7a792d67f..8ffe5d2d4 100644
--- a/Common.targets
+++ b/Common.targets
@@ -1,6 +1,6 @@
- 5.3.0
+ 5.3.1
preview
Avanade
Avanade
diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj b/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj
index de3d207c8..8f830406d 100644
--- a/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj
+++ b/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj
@@ -11,7 +11,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj b/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj
index 4c6d7fb50..42cf6bc15 100644
--- a/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj
+++ b/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj
@@ -8,6 +8,6 @@
-
+
\ No newline at end of file
diff --git a/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj b/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj
index 8f150e92d..5431f3037 100644
--- a/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj
+++ b/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj
@@ -35,7 +35,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj b/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj
index 913474932..0f75a8da8 100644
--- a/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj
+++ b/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj
@@ -15,13 +15,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj b/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj
index f907d7c4d..aadfcc4a3 100644
--- a/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj
+++ b/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj
@@ -7,7 +7,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj b/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj
index c1679e986..386e029a3 100644
--- a/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj
+++ b/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj
@@ -57,7 +57,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj b/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj
index 0a3d0b3c5..ca778f6e3 100644
--- a/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj
+++ b/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj
@@ -5,10 +5,10 @@
true
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj b/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj
index fec596da9..85600929f 100644
--- a/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj
+++ b/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj
@@ -4,6 +4,6 @@
enable
-
+
\ No newline at end of file
diff --git a/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj b/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj
index fdc965154..285214ee8 100644
--- a/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj
+++ b/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj
@@ -32,13 +32,13 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj b/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj
index c622ea2bc..94138d17e 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj
+++ b/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj
@@ -5,7 +5,7 @@
true
-
+
diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj b/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj
index beb9007fa..bfbf8de21 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj
+++ b/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj
@@ -5,10 +5,10 @@
true
-
-
-
-
+
+
+
+
diff --git a/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj b/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj
index fec596da9..85600929f 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj
+++ b/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj
@@ -4,6 +4,6 @@
enable
-
+
\ No newline at end of file
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj
index 67a843c99..28e9b9f0b 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj
@@ -16,8 +16,8 @@
-
-
+
+
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/OktaHttpClient.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/OktaHttpClient.cs
index 21dd55518..454deb894 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/OktaHttpClient.cs
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/OktaHttpClient.cs
@@ -15,7 +15,7 @@ public OktaHttpClient(HttpClient client, SecuritySettings settings, IJsonSeriali
public async Task GetUser(string email)
{
var response = await GetAsync>($"/api/v1/users?search=profile.email eq \"{email}\"").ConfigureAwait(false);
- return response.Value.SingleOrDefault();
+ return response.Value.Count == 1 ? response.Value[0] : null;
}
///
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs
index 0646a0940..31a925bfd 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs
@@ -8,7 +8,6 @@ public class SecuritySubscriberFunction
[Singleton(Mode = SingletonMode.Function)]
[FunctionName(nameof(SecuritySubscriberFunction))]
- [ExponentialBackoffRetry(3, "00:02:00", "00:30:00")]
public Task RunAsync([ServiceBusTrigger("%ServiceBusQueueName%", Connection = "ServiceBusConnectionString")] ServiceBusReceivedMessage message, ServiceBusMessageActions messageActions, CancellationToken cancellationToken)
=> _subscriber.ReceiveAsync(message, messageActions, cancellationToken);
}
\ No newline at end of file
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs
index f13f21b28..a11d90014 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs
@@ -24,7 +24,7 @@ public EmployeeTerminatedSubcriber(OktaHttpClient okta, ILogger @event, CancellationToken cancellationToken)
{
- var user = await _okta.GetUser(@event.Value.Email!).ConfigureAwait(false) ?? throw new NotFoundException($"Employee {@event.Value.Id} with email {@event.Value.Email} not found in OKTA.");
+ var user = await _okta.GetUser(@event.Value.Email!).ConfigureAwait(false) ?? throw new NotFoundException($"Employee {@event.Value.Id} with email {@event.Value.Email} either not found, or multiple exist, within OKTA.");
if (!user.IsDeactivatable)
_logger.LogWarning("Employee {EmployeeId} with email {Email} has User status of {UserStatus} and is therefore unable to be deactivated.", @event.Value.Id, @event.Value.Email, user.Status);
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/appsettings.json b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/appsettings.json
index 7f7bca8d9..e3f01ec0e 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/appsettings.json
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/appsettings.json
@@ -3,5 +3,9 @@
"BaseUri": "https://dev-1234.okta.com",
"HttpRetryCount": 2,
"HttpTimeoutSeconds": 120
+ },
+ "ServiceBusOrchestratedSubscriber": {
+ "AbandonOnTransient": true,
+ "RetryDelay": "00:00:05"
}
}
\ No newline at end of file
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json
index 141f90b17..84701a454 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json
@@ -15,9 +15,9 @@
"serviceBus": {
"clientRetryOptions": {
"mode": "exponential",
- "tryTimeout": "00:05:00",
- "delay": "00:00:30.00",
- "maxDelay": "00:03:00",
+ "tryTimeout": "00:01:00",
+ "delay": "00:00:00.80",
+ "maxDelay": "00:01:00",
"maxRetries": 3
},
"prefetchCount": 0,
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj b/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj
index 91f668ac1..35b6a310a 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj
@@ -28,7 +28,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs
index 8fe785d82..9cc43a80d 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs
@@ -7,30 +7,27 @@ public class SecuritySubscriberFunctionTest
public void InvalidMessage_DeadLetter()
{
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
+ var actions = test.CreateServiceBusMessageActions();
var message = test.CreateServiceBusMessage(null!);
test.ServiceBusTrigger()
- .ExpectLogContains("[EventDataDeserialization]")
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
+ .Run(f => f.RunAsync(message, actions, default))
.AssertSuccess();
- actionsMock.Verify(m => m.DeadLetterMessageAsync(message, It.IsAny(), It.IsAny(), default), Times.Once);
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertDeadLetter("EventDataDeserialization", "Cannot decode JSON element of kind 'Null' as CloudEvent");
}
[Test]
public void NotSubscribed_CompleteSilent()
{
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
+ var actions = test.CreateServiceBusMessageActions();
var message = test.CreateServiceBusMessage(new EventData { Subject = "myef.hr.employee", Action = "updated", Source = new Uri("test", UriKind.Relative) });
test.ServiceBusTrigger()
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
+ .Run(f => f.RunAsync(message, actions, default))
.AssertSuccess();
- actionsMock.Verify(m => m.CompleteMessageAsync(message, default), Times.Once);
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertComplete();
}
}
\ No newline at end of file
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs
index 14d576ab2..6e1286fa8 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs
@@ -1,6 +1,4 @@
-using UnitTestEx.Expectations;
-
-namespace MyEf.Hr.Security.Test.Subscribers;
+namespace MyEf.Hr.Security.Test.Subscribers;
[TestFixture]
public class EmployeeTerminatedSubscriberTest
@@ -9,32 +7,29 @@ public class EmployeeTerminatedSubscriberTest
public void ValueIsRequired_DeadLetter()
{
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
var message = test.CreateServiceBusMessage(new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = null! });
+ var actions = test.CreateServiceBusMessageActions();
test.ServiceBusTrigger()
- .ExpectLogContains("Value is required")
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
+ .Run(f => f.RunAsync(message, actions, default))
.AssertSuccess();
- actionsMock.Verify(m => m.DeadLetterMessageAsync(message, It.IsAny(), It.IsAny(), default), Times.Once);
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertDeadLetter("ValidationError", "Value is required");
}
[Test]
public void ValidationError_DeadLetter()
{
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
var message = test.CreateServiceBusMessage(new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee() });
+ var actions = test.CreateServiceBusMessageActions();
test.ServiceBusTrigger()
.ExpectLogContains("A data validation error occurred")
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
+ .Run(f => f.RunAsync(message, actions, default))
.AssertSuccess();
- actionsMock.Verify(m => m.DeadLetterMessageAsync(message, It.IsAny(), It.IsAny(), default), Times.Once);
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertDeadLetter("ValidationError", "Email is required");
}
[Test]
@@ -45,18 +40,17 @@ public void EmailNotFound_Complete()
mc.Request(HttpMethod.Get, "/api/v1/users?search=profile.email eq \"bob@email.com\"").Respond.WithJson("[]", HttpStatusCode.OK);
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
+ var actions = test.CreateServiceBusMessageActions();
var message = test.CreateServiceBusMessage(
new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } });
test.ReplaceHttpClientFactory(mcf)
.ServiceBusTrigger()
- .ExpectLogContains("email bob@email.com not found", "[Source: Subscriber, Handling: CompleteWithWarning]")
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
+ .ExpectLogContains("email bob@email.com either not found, or multiple exist, within OKTA.", "[Source: Subscriber, Handling: CompleteWithWarning]")
+ .Run(f => f.RunAsync(message, actions, default))
.AssertSuccess();
- actionsMock.Verify(m => m.CompleteMessageAsync(message, default), Times.Once);
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertComplete();
mcf.VerifyAll();
}
@@ -68,17 +62,17 @@ public void OktaForbidden_Retry()
mc.Request(HttpMethod.Get, "/api/v1/users?search=profile.email eq \"bob@email.com\"").Respond.With(HttpStatusCode.Forbidden);
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
+ var actions = test.CreateServiceBusMessageActions();
var message = test.CreateServiceBusMessage(
new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } });
test.ReplaceHttpClientFactory(mcf)
.ServiceBusTrigger()
.ExpectLogContains("Retry - Service Bus message", "[AuthenticationError]")
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
- .AssertException();
+ .Run(f => f.RunAsync(message, actions, default))
+ .AssertSuccess();
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertRenew(1).AssertAbandon();
mcf.VerifyAll();
}
@@ -95,17 +89,17 @@ public void OktaServiceUnavailable_Retry()
});
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
+ var actions = test.CreateServiceBusMessageActions();
var message = test.CreateServiceBusMessage(
new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } });
test.ReplaceHttpClientFactory(mcf)
.ServiceBusTrigger()
.ExpectLogContains("Retry - Service Bus message", "[TransientError]")
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
- .AssertException();
+ .Run(f => f.RunAsync(message, actions, default))
+ .AssertSuccess();
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertRenew(1).AssertAbandon();
mcf.VerifyAll();
}
@@ -118,17 +112,16 @@ public void SuccessDeactivated_Complete()
mc.Request(HttpMethod.Post, "/api/v1/users/00ub0oNGTSWTBKOLGLNR/lifecycle/deactivate?sendEmail=true").Respond.With(HttpStatusCode.OK);
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
+ var actions = test.CreateServiceBusMessageActions();
var message = test.CreateServiceBusMessage(
new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } });
test.ReplaceHttpClientFactory(mcf)
.ServiceBusTrigger()
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
+ .Run(f => f.RunAsync(message, actions, default))
.AssertSuccess();
- actionsMock.Verify(m => m.CompleteMessageAsync(message, default), Times.Once);
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertComplete();
mcf.VerifyAll();
}
@@ -140,18 +133,17 @@ public void SuccessAlreadyDeactivated_Complete()
mc.Request(HttpMethod.Get, "/api/v1/users?search=profile.email eq \"bob@email.com\"").Respond.WithJson(new [] { new { id = "00ub0oNGTSWTBKOLGLNR", status = "DEACTIVATED" } });
using var test = FunctionTester.Create();
- var actionsMock = new Mock();
+ var actions = test.CreateServiceBusMessageActions();
var message = test.CreateServiceBusMessage(
new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } });
test.ReplaceHttpClientFactory(mcf)
.ServiceBusTrigger()
.ExpectLogContains("warn: Employee 00000001-0000-0000-0000-000000000000 with email bob@email.com has User status of DEACTIVATED and is therefore unable to be deactivated.")
- .Run(f => f.RunAsync(message, actionsMock.Object, default))
+ .Run(f => f.RunAsync(message, actions, default))
.AssertSuccess();
- actionsMock.Verify(m => m.CompleteMessageAsync(message, default), Times.Once);
- actionsMock.VerifyNoOtherCalls();
+ actions.AssertComplete();
mcf.VerifyAll();
}
}
\ No newline at end of file
diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/appsettings.unittest.json b/samples/MyEf.Hr/MyEf.Hr.Security.Test/appsettings.unittest.json
index d2ee569b8..521e38059 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/appsettings.unittest.json
+++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/appsettings.unittest.json
@@ -1,5 +1,9 @@
{
"ConnectionStrings": {
"OktaApi": "https://test-okta.com"
+ },
+ "ServiceBusOrchestratedSubscriber": {
+ "AbandonOnTransient": true,
+ "RetryDelay": "00:00:00.5"
}
}
\ No newline at end of file
diff --git a/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj b/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj
index a1d2245b9..20c02f095 100644
--- a/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj
+++ b/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj
@@ -32,13 +32,13 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/samples/MyEf.Hr/docs/Service-Bus-Subscribe.md b/samples/MyEf.Hr/docs/Service-Bus-Subscribe.md
index 8265b1194..0606cac0c 100644
--- a/samples/MyEf.Hr/docs/Service-Bus-Subscribe.md
+++ b/samples/MyEf.Hr/docs/Service-Bus-Subscribe.md
@@ -87,7 +87,7 @@ Update project dependencies as follows.
Open the `host.json` file and replace with the contents from [`host.json`](../MyEf.Hr.Security.Subscriptions/host.json).
-The [configuration](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus) within will ensure that only a single message can be processed at a time, and also configures the basic retry policy.
+The [configuration](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus) within will ensure that _only_ a _single_ message can be processed at a time, and that there is also _no_ automatic completion of messages.
@@ -96,11 +96,15 @@ The [configuration](https://learn.microsoft.com/en-us/azure/azure-functions/func
Open the `local.settings.json` file and replace `Values` JSON with the following.
``` json
+{
+ "IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"ServiceBusConnectionString": "get-your-secret-and-paste-here",
"ServiceBusQueueName": "event-stream"
+ }
+}
```
@@ -218,6 +222,8 @@ Setting | Description
`OktaHttpClient:BaseUri` | The base [`Uri`](https://learn.microsoft.com/en-us/dotnet/api/system.uri) for the external OKTA API.
`OktaHttpClient:HttpRetryCount`* | Specifies the number of times the HTTP request should be retried when a transient error occurs.
`OktaHttpClient:HttpTimeoutSeconds`* | Specifies the maximum number of seconds for the HTTP request to complete before timing out.
+`ServiceBusOrchestratedSubscriber.AbandonOnTransient`* | Indicates that the message should be explicitly abandoned where transient error occurs.
+`ServiceBusOrchestratedSubscriber.RetryDelay`* | The timespan to delay (multiplied by delivery count) after each transient error is encountered; continues to lock message.
``` json
{
@@ -225,6 +231,10 @@ Setting | Description
"BaseUri": "https://dev-1234.okta.com",
"HttpRetryCount": 2,
"HttpTimeoutSeconds": 120
+ },
+ "ServiceBusOrchestratedSubscriber": {
+ "AbandonOnTransient": true,
+ "RetryDelay": "00:00:30"
}
}
```
@@ -263,7 +273,7 @@ public class OktaHttpClient : TypedHttpClientBase
public async Task GetUser(string email)
{
var response = await GetAsync>($"/api/v1/users?search=profile.email eq \"{email}\"").ConfigureAwait(false);
- return response.Value.SingleOrDefault();
+ return response.Value.Count == 1 ? response.Value[0] : null;
}
///
@@ -335,7 +345,7 @@ public class EmployeeTerminatedSubcriber : SubscriberBase
public override async Task ReceiveAsync(EventData @event, CancellationToken cancellationToken)
{
- var user = await _okta.GetUser(@event.Value.Email!).ConfigureAwait(false) ?? throw new NotFoundException($"Employee {@event.Value.Id} with email {@event.Value.Email} not found in OKTA.");
+ var user = await _okta.GetUser(@event.Value.Email!).ConfigureAwait(false) ?? throw new NotFoundException($"Employee {@event.Value.Id} with email {@event.Value.Email} either not found, or multiple exist, within OKTA.");
if (!user.IsDeactivatable)
_logger.LogWarning("Employee {EmployeeId} with email {Email} has User status of {UserStatus} and is therefore unable to be deactivated.", @event.Value.Id, @event.Value.Email, user.Status);
diff --git a/templates/Beef.Template.Solution/content/.template.config/template.json b/templates/Beef.Template.Solution/content/.template.config/template.json
index 75359907a..dd410b699 100644
--- a/templates/Beef.Template.Solution/content/.template.config/template.json
+++ b/templates/Beef.Template.Solution/content/.template.config/template.json
@@ -65,7 +65,7 @@
"type": "generated",
"generator": "constant",
"parameters": {
- "value": "2.9.0"
+ "value": "2.10.0"
},
"replaces": "CoreExVersion"
},
@@ -73,7 +73,7 @@
"type": "generated",
"generator": "constant",
"parameters": {
- "value": "2.2.1"
+ "value": "2.2.3"
},
"replaces": "UnitTestExVersion"
},
@@ -81,7 +81,7 @@
"type": "generated",
"generator": "constant",
"parameters": {
- "value": "5.3.0"
+ "value": "5.3.1"
},
"replaces": "BeefVersion"
},
diff --git a/tools/Beef.Database.Core/Beef.Database.Core.csproj b/tools/Beef.Database.Core/Beef.Database.Core.csproj
index e3e39d192..a2a464ec8 100644
--- a/tools/Beef.Database.Core/Beef.Database.Core.csproj
+++ b/tools/Beef.Database.Core/Beef.Database.Core.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/tools/Beef.Database.MySql/Beef.Database.MySql.csproj b/tools/Beef.Database.MySql/Beef.Database.MySql.csproj
index d04c3ddc2..488a640d0 100644
--- a/tools/Beef.Database.MySql/Beef.Database.MySql.csproj
+++ b/tools/Beef.Database.MySql/Beef.Database.MySql.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj b/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj
index 4d0da10a7..59645a673 100644
--- a/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj
+++ b/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj b/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj
index 058438f18..4db05b92a 100644
--- a/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj
+++ b/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj
@@ -8,8 +8,8 @@
-
-
+
+