diff --git a/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClient.cs b/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClient.cs index bd9f47498..5ec48ad6b 100644 --- a/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClient.cs +++ b/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClient.cs @@ -651,6 +651,11 @@ internal HttpManagementPayload CreateHttpManagementPayload( string taskHubName, string connectionName) { + if (this.httpApiHandler == null) + { + throw new InvalidOperationException("IDurableClient.CreateHttpManagementPayload is not supported for IDurableClient instances created outside of a Durable Functions application."); + } + return this.httpApiHandler.CreateHttpManagementPayload(instanceId, taskHubName, connectionName); } @@ -807,6 +812,11 @@ internal HttpResponseMessage CreateCheckStatusResponse( DurableClientAttribute attribute, bool returnInternalServerErrorOnFailure = false) { + if (this.httpApiHandler == null) + { + throw new InvalidOperationException("IDurableClient.CreateCheckStatusResponse is not supported for IDurableClient instances created outside of a Durable Functions application."); + } + return this.httpApiHandler.CreateCheckStatusResponse(request, instanceId, attribute, returnInternalServerErrorOnFailure); } diff --git a/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClientFactory.cs b/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClientFactory.cs index cc22f68dd..29699caad 100644 --- a/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClientFactory.cs +++ b/src/WebJobs.Extensions.DurableTask/ContextImplementations/DurableClientFactory.cs @@ -76,19 +76,12 @@ public IDurableClient CreateClient(DurableClientOptions durableClientOptions) DurableClientAttribute attribute = new DurableClientAttribute(durableClientOptions); - HttpApiHandler httpApiHandler = this.cachedHttpListeners.GetOrAdd( - attribute, - attr => - { - return new HttpApiHandler(null, null, this.durableTaskOptions, this.logger); - }); - DurableClient client = this.cachedClients.GetOrAdd( attribute, attr => { DurabilityProvider innerClient = this.durabilityProviderFactory.GetDurabilityProvider(attribute); - return new DurableClient(innerClient, httpApiHandler, attribute, this.MessageDataConverter, this.TraceHelper, this.durableTaskOptions); + return new DurableClient(innerClient, null, attribute, this.MessageDataConverter, this.TraceHelper, this.durableTaskOptions); }); return client; diff --git a/src/WebJobs.Extensions.DurableTask/DurableTaskJobHostConfigurationExtensions.cs b/src/WebJobs.Extensions.DurableTask/DurableTaskJobHostConfigurationExtensions.cs index 2afdcc8fb..9e763c58a 100644 --- a/src/WebJobs.Extensions.DurableTask/DurableTaskJobHostConfigurationExtensions.cs +++ b/src/WebJobs.Extensions.DurableTask/DurableTaskJobHostConfigurationExtensions.cs @@ -55,7 +55,7 @@ public static IWebJobsBuilder AddDurableTask(this IWebJobsBuilder builder) /// /// The to configure. /// Returns the provided . - public static IServiceCollection AddDurableTask(this IServiceCollection serviceCollection) + public static IServiceCollection AddDurableClientFactory(this IServiceCollection serviceCollection) { if (serviceCollection == null) { @@ -77,9 +77,9 @@ public static IServiceCollection AddDurableTask(this IServiceCollection serviceC /// The to configure. /// Populate default configurations of to create Durable Clients. /// Returns the provided . - public static IServiceCollection AddDurableTask(this IServiceCollection serviceCollection, Action optionsBuilder) + public static IServiceCollection AddDurableClientFactory(this IServiceCollection serviceCollection, Action optionsBuilder) { - AddDurableTask(serviceCollection); + AddDurableClientFactory(serviceCollection); serviceCollection.Configure(optionsBuilder.Invoke); return serviceCollection; } diff --git a/src/WebJobs.Extensions.DurableTask/Microsoft.Azure.WebJobs.Extensions.DurableTask.xml b/src/WebJobs.Extensions.DurableTask/Microsoft.Azure.WebJobs.Extensions.DurableTask.xml index c2a3102dd..00840c440 100644 --- a/src/WebJobs.Extensions.DurableTask/Microsoft.Azure.WebJobs.Extensions.DurableTask.xml +++ b/src/WebJobs.Extensions.DurableTask/Microsoft.Azure.WebJobs.Extensions.DurableTask.xml @@ -2162,14 +2162,14 @@ The to configure. Returns the provided . - + Adds the Durable Task extension to the provided . The to configure. Returns the provided . - + Adds the Durable Task extension to the provided . diff --git a/test/Common/DurableClientBaseTests.cs b/test/Common/DurableClientBaseTests.cs index 4d46b00db..0190a09de 100644 --- a/test/Common/DurableClientBaseTests.cs +++ b/test/Common/DurableClientBaseTests.cs @@ -11,12 +11,14 @@ #if !FUNCTIONS_V1 using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs.Extensions.DurableTask.Options; #endif using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Moq; +using Newtonsoft.Json; using Xunit; using static Microsoft.Azure.WebJobs.Extensions.DurableTask.Tests.HttpApiHandlerTests; @@ -142,6 +144,91 @@ public async Task TerminateAsync_NonRunningOrchestrator_ThrowsException() } #if !FUNCTIONS_V1 + [Fact] + [Trait("Category", PlatformSpecificHelpers.TestCategory)] + public async Task DurableClient_ExternalApp_StartNewAsync_ReturnsInstanceId() + { + var orchestrationServiceClientMock = new Mock(); + orchestrationServiceClientMock.Setup(x => x.GetOrchestrationStateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(GetInstanceState(OrchestrationStatus.Running)); + + var durableOrchestrationClient = this.GetDurableClient(orchestrationServiceClientMock.Object); + + var response = await durableOrchestrationClient.StartNewAsync("orchestrationName", "testInstanceId"); + Assert.Equal("testInstanceId", response); + } + + [Fact] + [Trait("Category", PlatformSpecificHelpers.TestCategory)] + public async void DurableClient_ExternalApp_GetStatusAsync_ReturnsStatus() + { + var orchestrationServiceClientMock = new Mock(); + orchestrationServiceClientMock.Setup(x => x.GetOrchestrationStateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(GetInstanceState(OrchestrationStatus.Running)); + + var durableOrchestrationClient = this.GetDurableClient(orchestrationServiceClientMock.Object); + var status = await durableOrchestrationClient.GetStatusAsync("testInstanceId"); + Assert.Equal(OrchestrationRuntimeStatus.Running, status.RuntimeStatus); + } + + [Fact] + [Trait("Category", PlatformSpecificHelpers.TestCategory)] + public async void DurableClient_ExternalApp_TerminateAsync_TerminateEventPlaced() + { + var orchestrationServiceClientMock = new Mock(); + orchestrationServiceClientMock.Setup(x => x.GetOrchestrationStateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(GetInstanceState(OrchestrationStatus.Running)); + + var durableOrchestrationClient = this.GetDurableClient(orchestrationServiceClientMock.Object); + await durableOrchestrationClient.TerminateAsync("valid_instance_id", "any reason"); + orchestrationServiceClientMock.Verify(x => x.ForceTerminateTaskOrchestrationAsync("valid_instance_id", "any reason"), Times.Once()); + } + + [Fact] + [Trait("Category", PlatformSpecificHelpers.TestCategory)] + public void DurableClient_ExternalApp_CreateCheckStatusResponse_ThrowsException() + { + var instanceId = Guid.NewGuid().ToString(); + + var orchestrationServiceClientMock = new Mock(); + orchestrationServiceClientMock.Setup(x => x.GetOrchestrationStateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(GetInstanceState(OrchestrationStatus.Running)); + + var durableOrchestrationClient = this.GetDurableClient(orchestrationServiceClientMock.Object); + Assert.ThrowsAny(() => durableOrchestrationClient.CreateCheckStatusResponse(new HttpRequestMessage(), "testInstanceId")); + } + + [Fact] + [Trait("Category", PlatformSpecificHelpers.TestCategory)] + public void DurableClient_ExternalApp_CreateHttpManagementPayload_ThrowsException() + { + var instanceId = Guid.NewGuid().ToString(); + + var orchestrationServiceClientMock = new Mock(); + orchestrationServiceClientMock.Setup(x => x.GetOrchestrationStateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(GetInstanceState(OrchestrationStatus.Running)); + + var durableOrchestrationClient = this.GetDurableClient(orchestrationServiceClientMock.Object); + Assert.ThrowsAny(() => durableOrchestrationClient.CreateHttpManagementPayload("testInstanceId")); + } + + private IDurableOrchestrationClient GetDurableClient(IOrchestrationServiceClient orchestrationServiceClientMockObject) + { + var storageProvider = new DurabilityProvider("test", new Mock().Object, orchestrationServiceClientMockObject, "test"); + DurableClientOptions durableClientOptions = new DurableClientOptions + { + ConnectionName = "Storage", + TaskHub = "TestTaskHub", + }; + DurableTaskOptions durableTaskOptions = new DurableTaskOptions(); + DurableClientAttribute attribute = new DurableClientAttribute(durableClientOptions); + MessagePayloadDataConverter messagePayloadDataConverter = new MessagePayloadDataConverter(new JsonSerializerSettings(), true); + var traceHelper = new EndToEndTraceHelper(new NullLogger(), durableTaskOptions.Tracing.TraceReplayEvents); + + var durableOrchestrationClient = (IDurableOrchestrationClient)new DurableClient(storageProvider, null, attribute, messagePayloadDataConverter, traceHelper, durableTaskOptions); + return durableOrchestrationClient; + } + [Fact] [Trait("Category", PlatformSpecificHelpers.TestCategory)] public async Task HttpRequest_HttpRequestMessage_ClientMethods_Identical()