From 0643d764f6015b8f6dfb6a792c0a9aada914de5c Mon Sep 17 00:00:00 2001 From: Ebbe Knudsen Date: Tue, 19 Nov 2024 20:04:31 +0100 Subject: [PATCH] feat: Add request calculated data client to PM (#1363) * Add request calculated data client to PM * Refactor process manager client DI extensions * Update http extensions * Add RequestCalculatedEnergyTimeSeriesTriggerV1 * Add test (WIP) * Add service bus topic options to orchestration * Remove required options (for now) * Add methods for brs descriptions --- .../ReleaseNotes/ReleaseNotes.md | 4 + .../RequestCalculatedEnergyTimeSeriesTests.cs | 74 ++++++++++++++++ .../MonitorCalculationUsingClientsScenario.cs | 6 +- .../DependencyInjection/ClientExtensions.cs | 48 ++++++++-- ...cs => ProcessManagerHttpClientsOptions.cs} | 6 +- .../ProcessManagerServiceBusClientsOptions.cs | 32 +++++++ .../ProcessManager.Client.csproj | 21 ++++- .../V1/IRequestCalculatedDataClientV1.cs | 34 ++++++++ .../V1/Model/RequestCalculatedDataInputV1.cs | 20 +++++ .../V1/RequestCalculatedDataClientV1.cs | 87 +++++++++++++++++++ .../ProcessManagerTopicExtensions.cs | 34 ++++++++ .../Options/ProcessManagerTopicOptions.cs | 42 +++++++++ .../ProcessManager.Orchestrations.csproj | 22 ++++- ...equestCalculatedEnergyTimeSeriesHandler.cs | 37 ++++++++ ...lculatedEnergyTimeSeriesOrchestrationV1.cs | 48 ++++++++++ ...uestCalculatedEnergyTimeSeriesTriggerV1.cs | 75 ++++++++++++++++ .../ProcessManager.Orchestrations/Program.cs | 53 ++++++++--- .../local.settings.sample.json | 7 +- .../Contracts/StartOrchestrationDto.proto | 24 +++++ ...equestCalculatedEnergyTimeSeriesInputV1.cs | 18 ++++ 20 files changed, 662 insertions(+), 30 deletions(-) create mode 100644 source/ProcessManager.Client.Tests/Integration/BRS_026_028/V1/RequestCalculatedEnergyTimeSeriesTests.cs rename source/ProcessManager.Client/Extensions/Options/{ProcessManagerClientOptions.cs => ProcessManagerHttpClientsOptions.cs} (84%) create mode 100644 source/ProcessManager.Client/Extensions/Options/ProcessManagerServiceBusClientsOptions.cs create mode 100644 source/ProcessManager.Client/Processes/BRS_026_028/V1/IRequestCalculatedDataClientV1.cs create mode 100644 source/ProcessManager.Client/Processes/BRS_026_028/V1/Model/RequestCalculatedDataInputV1.cs create mode 100644 source/ProcessManager.Client/Processes/BRS_026_028/V1/RequestCalculatedDataClientV1.cs create mode 100644 source/ProcessManager.Orchestrations/Extensions/DependencyInjection/ProcessManagerTopicExtensions.cs create mode 100644 source/ProcessManager.Orchestrations/Extensions/Options/ProcessManagerTopicOptions.cs create mode 100644 source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesHandler.cs create mode 100644 source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesOrchestrationV1.cs create mode 100644 source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesTriggerV1.cs create mode 100644 source/Shared/ProcessManager/Orchestrations/Contracts/StartOrchestrationDto.proto create mode 100644 source/Shared/ProcessManager/Orchestrations/Processes/BRS_026/V1/Model/RequestCalculatedEnergyTimeSeriesInputV1.cs diff --git a/docs/ProcessManager.Client/ReleaseNotes/ReleaseNotes.md b/docs/ProcessManager.Client/ReleaseNotes/ReleaseNotes.md index 97ef003ca6..8e01c1f293 100644 --- a/docs/ProcessManager.Client/ReleaseNotes/ReleaseNotes.md +++ b/docs/ProcessManager.Client/ReleaseNotes/ReleaseNotes.md @@ -1,5 +1,9 @@ # ProcessManager.Client Release Notes +## Version 0.10.0 + +- Added 'RequestCalculatedDataClientV1' + ## Version 0.9.3 - Updated 'NotifyAggregatedMeasureDataInputV1' with new required 'UserId' parameter/property. diff --git a/source/ProcessManager.Client.Tests/Integration/BRS_026_028/V1/RequestCalculatedEnergyTimeSeriesTests.cs b/source/ProcessManager.Client.Tests/Integration/BRS_026_028/V1/RequestCalculatedEnergyTimeSeriesTests.cs new file mode 100644 index 0000000000..3a068c2556 --- /dev/null +++ b/source/ProcessManager.Client.Tests/Integration/BRS_026_028/V1/RequestCalculatedEnergyTimeSeriesTests.cs @@ -0,0 +1,74 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using Energinet.DataHub.ProcessManager.Client.Tests.Fixtures; +using Xunit.Abstractions; + +namespace Energinet.DataHub.ProcessManager.Client.Tests.Integration.BRS_026_028.V1; + +/// +/// Test collection that verifies the Process Manager clients can be used to start a +/// request calculated energy time series orchestration and monitor its status during its lifetime. +/// +[Collection(nameof(ProcessManagerClientCollection))] +public class RequestCalculatedEnergyTimeSeriesTests : IAsyncLifetime +{ + private readonly ScenarioProcessManagerAppFixture _processManagerAppFixture; + private readonly ScenarioOrchestrationsAppFixture _orchestrationsAppFixture; + + public RequestCalculatedEnergyTimeSeriesTests( + ScenarioProcessManagerAppFixture processManagerAppFixture, + ScenarioOrchestrationsAppFixture orchestrationsAppFixture, + ITestOutputHelper testOutputHelper) + { + _processManagerAppFixture = processManagerAppFixture; + _orchestrationsAppFixture = orchestrationsAppFixture; + + _processManagerAppFixture.SetTestOutputHelper(testOutputHelper); + _orchestrationsAppFixture.SetTestOutputHelper(testOutputHelper); + } + + public Task InitializeAsync() + { + _processManagerAppFixture.AppHostManager.ClearHostLog(); + _orchestrationsAppFixture.AppHostManager.ClearHostLog(); + + return Task.CompletedTask; + } + + public Task DisposeAsync() + { + _processManagerAppFixture.SetTestOutputHelper(null!); + _orchestrationsAppFixture.SetTestOutputHelper(null!); + + return Task.CompletedTask; + } + + [Fact] + public async Task RequestCalculatedEnergyTimeSeries_WhenStartedUsingClient_CanMonitorLifecycle() + { + // TODO: Implement test after implementation of shared Service Bus topic in app fixtures + // Arrange + // var requestCalculatedDataClient = new RequestCalculatedDataClientV1(); + // var input = new RequestCalculatedDataInputV1( + // Guid.NewGuid().ToString(), + // new RequestCalculatedEnergyTimeSeriesInputV1("B1337")); + // + // // Act + // await requestCalculatedDataClient.RequestCalculatedEnergyTimeSeriesAsync(input, CancellationToken.None); + + // Assert + await Task.CompletedTask; + } +} diff --git a/source/ProcessManager.Client.Tests/Integration/MonitorCalculationUsingClientsScenario.cs b/source/ProcessManager.Client.Tests/Integration/MonitorCalculationUsingClientsScenario.cs index e2c428c984..a32109bf83 100644 --- a/source/ProcessManager.Client.Tests/Integration/MonitorCalculationUsingClientsScenario.cs +++ b/source/ProcessManager.Client.Tests/Integration/MonitorCalculationUsingClientsScenario.cs @@ -53,12 +53,12 @@ public MonitorCalculationUsingClientsScenario( var services = new ServiceCollection(); services.AddScoped(_ => CreateInMemoryConfigurations(new Dictionary() { - [$"{ProcessManagerClientOptions.SectionName}:{nameof(ProcessManagerClientOptions.GeneralApiBaseAddress)}"] + [$"{ProcessManagerHttpClientsOptions.SectionName}:{nameof(ProcessManagerHttpClientsOptions.GeneralApiBaseAddress)}"] = ProcessManagerAppFixture.AppHostManager.HttpClient.BaseAddress!.ToString(), - [$"{ProcessManagerClientOptions.SectionName}:{nameof(ProcessManagerClientOptions.OrchestrationsApiBaseAddress)}"] + [$"{ProcessManagerHttpClientsOptions.SectionName}:{nameof(ProcessManagerHttpClientsOptions.OrchestrationsApiBaseAddress)}"] = OrchestrationsAppFixture.AppHostManager.HttpClient.BaseAddress!.ToString(), })); - services.AddProcessManagerClients(); + services.AddProcessManagerHttpClients(); ServiceProvider = services.BuildServiceProvider(); } diff --git a/source/ProcessManager.Client/Extensions/DependencyInjection/ClientExtensions.cs b/source/ProcessManager.Client/Extensions/DependencyInjection/ClientExtensions.cs index 417eb66b8b..b229f6ddb0 100644 --- a/source/ProcessManager.Client/Extensions/DependencyInjection/ClientExtensions.cs +++ b/source/ProcessManager.Client/Extensions/DependencyInjection/ClientExtensions.cs @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +using Azure.Messaging.ServiceBus; using Energinet.DataHub.ProcessManager.Client.Extensions.Options; using Energinet.DataHub.ProcessManager.Client.Processes.BRS_023_027.V1; +using Energinet.DataHub.ProcessManager.Client.Processes.BRS_026_028.V1; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Azure; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -27,25 +31,25 @@ namespace Energinet.DataHub.ProcessManager.Client.Extensions.DependencyInjection public static class ClientExtensions { /// - /// Register Process Manager clients for use in applications. + /// Register Process Manager HTTP clients for use in applications. /// If is registered we try to retrieve the "Authorization" /// header value and forward it to the Process Manager API for authentication/authorization. /// - public static IServiceCollection AddProcessManagerClients(this IServiceCollection services) + public static IServiceCollection AddProcessManagerHttpClients(this IServiceCollection services) { services - .AddOptions() - .BindConfiguration(ProcessManagerClientOptions.SectionName) + .AddOptions() + .BindConfiguration(ProcessManagerHttpClientsOptions.SectionName) .ValidateDataAnnotations(); services.AddHttpClient(HttpClientNames.GeneralApi, (sp, httpClient) => { - var options = sp.GetRequiredService>().Value; + var options = sp.GetRequiredService>().Value; ConfigureHttpClient(sp, httpClient, options.GeneralApiBaseAddress); }); services.AddHttpClient(HttpClientNames.OrchestrationsApi, (sp, httpClient) => { - var options = sp.GetRequiredService>().Value; + var options = sp.GetRequiredService>().Value; ConfigureHttpClient(sp, httpClient, options.OrchestrationsApiBaseAddress); }); @@ -55,6 +59,38 @@ public static IServiceCollection AddProcessManagerClients(this IServiceCollectio return services; } + /// + /// Register Process Manager RequestCalculatedData client for use in applications. + /// The application must register the and contain configuration for + /// + public static IServiceCollection AddProcessManagerRequestCalculatedDataClient(this IServiceCollection services) + { + services + .AddOptions() + .BindConfiguration(ProcessManagerServiceBusClientsOptions.SectionName) + .ValidateDataAnnotations(); + + services.AddAzureClients( + builder => + { + builder.AddClient( + (_, _, provider) => + { + var serviceBusOptions = provider.GetRequiredService>().Value; + var serviceBusSender = provider + .GetRequiredService() + .CreateSender(serviceBusOptions.TopicName); + + return serviceBusSender; + }) + .WithName(nameof(ProcessManagerServiceBusClientsOptions.TopicName)); + }); + + services.AddScoped(); + + return services; + } + /// /// Configure http client base address; and if available then apply /// the authorization header from the current HTTP context. diff --git a/source/ProcessManager.Client/Extensions/Options/ProcessManagerClientOptions.cs b/source/ProcessManager.Client/Extensions/Options/ProcessManagerHttpClientsOptions.cs similarity index 84% rename from source/ProcessManager.Client/Extensions/Options/ProcessManagerClientOptions.cs rename to source/ProcessManager.Client/Extensions/Options/ProcessManagerHttpClientsOptions.cs index 0b9cb403f5..03bbc216cb 100644 --- a/source/ProcessManager.Client/Extensions/Options/ProcessManagerClientOptions.cs +++ b/source/ProcessManager.Client/Extensions/Options/ProcessManagerHttpClientsOptions.cs @@ -17,11 +17,11 @@ namespace Energinet.DataHub.ProcessManager.Client.Extensions.Options; /// -/// Options for the configuration of Process Manager clients using the Process Manager API. +/// Options for the configuration of Process Manager HTTP clients using the Process Manager API. /// -public class ProcessManagerClientOptions +public class ProcessManagerHttpClientsOptions { - public const string SectionName = "ProcessManagerClient"; + public const string SectionName = "ProcessManagerHttpClients"; /// /// Address to the general Api hosted in Process Manager. diff --git a/source/ProcessManager.Client/Extensions/Options/ProcessManagerServiceBusClientsOptions.cs b/source/ProcessManager.Client/Extensions/Options/ProcessManagerServiceBusClientsOptions.cs new file mode 100644 index 0000000000..62ac7f483b --- /dev/null +++ b/source/ProcessManager.Client/Extensions/Options/ProcessManagerServiceBusClientsOptions.cs @@ -0,0 +1,32 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.Configuration; + +namespace Energinet.DataHub.ProcessManager.Client.Extensions.Options; + +/// +/// Options for configuration of Process Manager Service Bus clients using the Process Manager. +/// +public class ProcessManagerServiceBusClientsOptions +{ + public const string SectionName = "ProcessManagerServiceBusClients"; + + /// + /// Name of the topic which the Process Manager receives service bus messages on + /// + [Required] + public string TopicName { get; set; } = string.Empty; +} diff --git a/source/ProcessManager.Client/ProcessManager.Client.csproj b/source/ProcessManager.Client/ProcessManager.Client.csproj index 45b56576d7..8cc2d9ddaf 100644 --- a/source/ProcessManager.Client/ProcessManager.Client.csproj +++ b/source/ProcessManager.Client/ProcessManager.Client.csproj @@ -7,7 +7,7 @@ Energinet.DataHub.ProcessManager.Client - 0.9.3$(VersionSuffix) + 0.10.0$(VersionSuffix) DH3 Process Manager Client library Energinet-DataHub Energinet-DataHub @@ -62,9 +62,14 @@ + + Processes\BRS_026_028\V1\Model\RequestCalculatedEnergyTimeSeriesInputV1.cs + + + @@ -72,6 +77,20 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + None + Public + True + True + MSBuild:Compile + diff --git a/source/ProcessManager.Client/Processes/BRS_026_028/V1/IRequestCalculatedDataClientV1.cs b/source/ProcessManager.Client/Processes/BRS_026_028/V1/IRequestCalculatedDataClientV1.cs new file mode 100644 index 0000000000..2f3b484128 --- /dev/null +++ b/source/ProcessManager.Client/Processes/BRS_026_028/V1/IRequestCalculatedDataClientV1.cs @@ -0,0 +1,34 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using Energinet.DataHub.ProcessManager.Client.Processes.BRS_026_028.V1.Model; +using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1.Models; + +namespace Energinet.DataHub.ProcessManager.Client.Processes.BRS_026_028.V1; + +/// +/// Client for the BRS-026/BRS-028 process +/// +public interface IRequestCalculatedDataClientV1 +{ + /// + /// Start a request for energy results + /// + public Task RequestCalculatedEnergyTimeSeriesAsync(RequestCalculatedDataInputV1 input, CancellationToken cancellationToken); + + /// + /// Start a request for wholesale results + /// + public Task RequestCalculatedWholesaleServicesAsync(RequestCalculatedDataInputV1 input, CancellationToken cancellationToken); +} diff --git a/source/ProcessManager.Client/Processes/BRS_026_028/V1/Model/RequestCalculatedDataInputV1.cs b/source/ProcessManager.Client/Processes/BRS_026_028/V1/Model/RequestCalculatedDataInputV1.cs new file mode 100644 index 0000000000..e18bffa3f7 --- /dev/null +++ b/source/ProcessManager.Client/Processes/BRS_026_028/V1/Model/RequestCalculatedDataInputV1.cs @@ -0,0 +1,20 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +namespace Energinet.DataHub.ProcessManager.Client.Processes.BRS_026_028.V1.Model; + +public record RequestCalculatedDataInputV1( + string MessageId, + TInput Input) + where TInput : class; diff --git a/source/ProcessManager.Client/Processes/BRS_026_028/V1/RequestCalculatedDataClientV1.cs b/source/ProcessManager.Client/Processes/BRS_026_028/V1/RequestCalculatedDataClientV1.cs new file mode 100644 index 0000000000..1168476f96 --- /dev/null +++ b/source/ProcessManager.Client/Processes/BRS_026_028/V1/RequestCalculatedDataClientV1.cs @@ -0,0 +1,87 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using System.Dynamic; +using System.Text.Json; +using Azure.Messaging.ServiceBus; +using Energinet.DataHub.ProcessManager.Client.Extensions.Options; +using Energinet.DataHub.ProcessManager.Client.Processes.BRS_026_028.V1.Model; +using Energinet.DataHub.ProcessManager.Orchestrations.Contracts; +using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1.Models; +using Google.Protobuf; +using Microsoft.Extensions.Azure; + +namespace Energinet.DataHub.ProcessManager.Client.Processes.BRS_026_028.V1; + +public class RequestCalculatedDataClientV1( + IAzureClientFactory serviceBusFactory) + : IRequestCalculatedDataClientV1 +{ + private readonly ServiceBusSender _serviceBusSender = serviceBusFactory.CreateClient(nameof(ProcessManagerServiceBusClientsOptions.TopicName)); + + public async Task RequestCalculatedEnergyTimeSeriesAsync(RequestCalculatedDataInputV1 input, CancellationToken cancellationToken) + { + var serviceBusMessage = CreateServiceBusMessage( + "BRS_026", + 1, + input); + + await SendServiceBusMessage( + serviceBusMessage, + cancellationToken) + .ConfigureAwait(false); + } + + public async Task RequestCalculatedWholesaleServicesAsync(RequestCalculatedDataInputV1 input, CancellationToken cancellationToken) + { + var serviceBusMessage = CreateServiceBusMessage( + "BRS_028", + 1, + input); + + await SendServiceBusMessage(serviceBusMessage, cancellationToken) + .ConfigureAwait(false); + } + + private ServiceBusMessage CreateServiceBusMessage( + string orchestrationName, + int orchestrationVersion, + RequestCalculatedDataInputV1 input) + where TInput : class + { + var message = new StartOrchestrationDto + { + OrchestrationName = orchestrationName, + OrchestrationVersion = orchestrationVersion, + JsonInput = JsonSerializer.Serialize(input.Input), + }; + + ServiceBusMessage serviceBusMessage = new(JsonFormatter.Default.Format(message)) + { + Subject = orchestrationName, + MessageId = input.MessageId, + ContentType = "application/json", + }; + + return serviceBusMessage; + } + + private async Task SendServiceBusMessage( + ServiceBusMessage serviceBusMessage, + CancellationToken cancellationToken) + { + await _serviceBusSender.SendMessageAsync(serviceBusMessage, cancellationToken) + .ConfigureAwait(false); + } +} diff --git a/source/ProcessManager.Orchestrations/Extensions/DependencyInjection/ProcessManagerTopicExtensions.cs b/source/ProcessManager.Orchestrations/Extensions/DependencyInjection/ProcessManagerTopicExtensions.cs new file mode 100644 index 0000000000..aa30801699 --- /dev/null +++ b/source/ProcessManager.Orchestrations/Extensions/DependencyInjection/ProcessManagerTopicExtensions.cs @@ -0,0 +1,34 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using Energinet.DataHub.ProcessManager.Orchestrations.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; + +namespace Energinet.DataHub.ProcessManager.Orchestrations.Extensions.DependencyInjection; + +public static class ProcessManagerTopicExtensions +{ + /// + /// Add required dependencies to use the Process Manager Service Bus topic. + /// + public static IServiceCollection AddProcessManagerTopic(this IServiceCollection services) + { + services + .AddOptionsWithValidateOnStart() + .BindConfiguration(ProcessManagerTopicOptions.SectionName) + .ValidateDataAnnotations(); + + return services; + } +} diff --git a/source/ProcessManager.Orchestrations/Extensions/Options/ProcessManagerTopicOptions.cs b/source/ProcessManager.Orchestrations/Extensions/Options/ProcessManagerTopicOptions.cs new file mode 100644 index 0000000000..02f100503e --- /dev/null +++ b/source/ProcessManager.Orchestrations/Extensions/Options/ProcessManagerTopicOptions.cs @@ -0,0 +1,42 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.Configuration; + +namespace Energinet.DataHub.ProcessManager.Orchestrations.Extensions.Options; + +/// +/// Contains options required for the orchestrations app to connect to the +/// ProcessManager Service Bus topic. +/// +public class ProcessManagerTopicOptions +{ + /// + /// Name of the section in the / appsettings.json file + /// + public const string SectionName = "ProcessManagerTopic"; + + /// + /// Name of the ProcessManager Service Bus topic + /// + //[Required] // TODO: Removed required for now since tests cannot be run (yet) + public string TopicName { get; } = string.Empty; + + /// + /// Name of the subscription for BRS026 to the ProcessManager Service Bus topic + /// + //[Required] // TODO: Removed required for now since tests cannot be run (yet) + public string Brs026SubscriptionName { get; } = string.Empty; +} diff --git a/source/ProcessManager.Orchestrations/ProcessManager.Orchestrations.csproj b/source/ProcessManager.Orchestrations/ProcessManager.Orchestrations.csproj index 3d188c74ed..f097d9b8b8 100644 --- a/source/ProcessManager.Orchestrations/ProcessManager.Orchestrations.csproj +++ b/source/ProcessManager.Orchestrations/ProcessManager.Orchestrations.csproj @@ -7,9 +7,16 @@ + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -29,11 +36,24 @@ - + + + Processes\BRS_026\V1\Models\RequestCalculatedEnergyTimeSeriesInputV1.cs + + + + + + None + Public + True + True + MSBuild:Compile + \ No newline at end of file diff --git a/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesHandler.cs b/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesHandler.cs new file mode 100644 index 0000000000..0743cbcfce --- /dev/null +++ b/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesHandler.cs @@ -0,0 +1,37 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using Energinet.DataHub.ProcessManagement.Core.Application.Orchestration; +using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1.Models; + +namespace Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1; + +public class RequestCalculatedEnergyTimeSeriesHandler( + IStartOrchestrationInstanceCommands commands) +{ + private readonly IStartOrchestrationInstanceCommands _commands = commands; + + /// + /// Start a request for calculated energy time series. + /// + public async Task StartRequestCalculatedEnergyTimeSeriesAsync(RequestCalculatedEnergyTimeSeriesInputV1 input) + { + await _commands.StartNewOrchestrationInstanceAsync( + "BRS_026", + 1, + input, + []) + .ConfigureAwait(false); + } +} diff --git a/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesOrchestrationV1.cs b/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesOrchestrationV1.cs new file mode 100644 index 0000000000..bcdcc9452c --- /dev/null +++ b/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesOrchestrationV1.cs @@ -0,0 +1,48 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using Energinet.DataHub.ProcessManagement.Core.Infrastructure.Extensions.DurableTask; +using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1.Models; +using Microsoft.Azure.Functions.Worker; +using Microsoft.DurableTask; + +namespace Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1; + +// TODO: Implement according to guidelines: https://energinet.atlassian.net/wiki/spaces/D3/pages/824803345/Durable+Functions+Development+Guidelines +internal class RequestCalculatedEnergyTimeSeriesOrchestrationV1 +{ + [Function(nameof(RequestCalculatedEnergyTimeSeriesOrchestrationV1))] + public async Task Run( + [OrchestrationTrigger] TaskOrchestrationContext context) + { + var input = context.GetOrchestrationParameterValue(); + + if (input == null) + return "Error: No input specified."; + + await Task.CompletedTask; + + /* + * Steps: + * 1. Deserialize input + * 2. Async validation + * 3. Query databricks and upload to storage account + * 4. Enqueue Messages in EDI + * 5. Wait for notify from EDI + * 6. Complete process in database + */ + + return $"Success (BusinessReason={input.BusinessReason})"; + } +} diff --git a/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesTriggerV1.cs b/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesTriggerV1.cs new file mode 100644 index 0000000000..c0f624363e --- /dev/null +++ b/source/ProcessManager.Orchestrations/Processes/BRS_026/V1/RequestCalculatedEnergyTimeSeriesTriggerV1.cs @@ -0,0 +1,75 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +using System.Text.Json; +using Azure.Messaging.ServiceBus; +using Energinet.DataHub.Core.Messaging.Communication.Extensions.Options; +using Energinet.DataHub.ProcessManager.Orchestrations.Contracts; +using Energinet.DataHub.ProcessManager.Orchestrations.Extensions.Options; +using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1.Models; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Logging; + +namespace Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1; + +public class RequestCalculatedEnergyTimeSeriesTriggerV1( + ILogger logger, + RequestCalculatedEnergyTimeSeriesHandler handler) +{ + private readonly ILogger _logger = logger; + private readonly RequestCalculatedEnergyTimeSeriesHandler _handler = handler; + + /// + /// Start a BRS-026 request. + /// + [Function(nameof(RequestCalculatedEnergyTimeSeriesTriggerV1))] + public async Task Run( + [ServiceBusTrigger( + $"%{ProcessManagerTopicOptions.SectionName}:{nameof(ProcessManagerTopicOptions.TopicName)}%", + $"%{ProcessManagerTopicOptions.SectionName}:{nameof(ProcessManagerTopicOptions.Brs026SubscriptionName)}%", + Connection = ServiceBusNamespaceOptions.SectionName)] + ServiceBusReceivedMessage message) + { + using var serviceBusMessageLoggerScope = _logger.BeginScope(new + { + ServiceBusMessage = new + { + message.MessageId, + message.CorrelationId, + message.Subject, + }, + }); + + var jsonMessage = message.Body.ToString(); + var startOrchestrationDto = StartOrchestrationDto.Parser.ParseJson(jsonMessage); + using var startOrchestrationLoggerScope = _logger.BeginScope(new + { + StartOrchestration = new + { + startOrchestrationDto.OrchestrationName, + startOrchestrationDto.OrchestrationVersion, + }, + }); + + var requestCalculatedEnergyTimeSeriesDto = JsonSerializer.Deserialize(startOrchestrationDto.JsonInput); + if (requestCalculatedEnergyTimeSeriesDto is null) + { + _logger.LogWarning($"Unable to deserialize {nameof(startOrchestrationDto.JsonInput)} to {nameof(RequestCalculatedEnergyTimeSeriesInputV1)} type:{Environment.NewLine}{0}", startOrchestrationDto.JsonInput); + throw new ArgumentException($"Unable to deserialize {nameof(startOrchestrationDto.JsonInput)} to {nameof(RequestCalculatedEnergyTimeSeriesInputV1)} type"); + } + + await _handler.StartRequestCalculatedEnergyTimeSeriesAsync(requestCalculatedEnergyTimeSeriesDto) + .ConfigureAwait(false); + } +} diff --git a/source/ProcessManager.Orchestrations/Program.cs b/source/ProcessManager.Orchestrations/Program.cs index cb279f1054..0d46225e22 100644 --- a/source/ProcessManager.Orchestrations/Program.cs +++ b/source/ProcessManager.Orchestrations/Program.cs @@ -19,8 +19,11 @@ using Energinet.DataHub.ProcessManagement.Core.Infrastructure.Extensions.DependencyInjection; using Energinet.DataHub.ProcessManagement.Core.Infrastructure.Extensions.Startup; using Energinet.DataHub.ProcessManagement.Core.Infrastructure.Telemetry; +using Energinet.DataHub.ProcessManager.Orchestrations.Extensions.DependencyInjection; using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_023_027.V1; using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_023_027.V1.Model; +using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1; +using Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1.Models; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -34,6 +37,7 @@ services.AddNodaTimeForApplication(); // ProcessManager + services.AddProcessManagerTopic(); // => Orchestration Descriptions services.AddProcessManagerForOrchestrations(() => { @@ -41,21 +45,10 @@ // We could implement an interface for "description building" which could then be implemented besides the orchestration. // During DI we could then search for all these interface implementations and register them automatically. // This would ensure we didn't have to update Program.cs when we change orchestrations. - var brs_023_027_v1 = new OrchestrationDescription( - name: "BRS_023_027", - version: 1, - canBeScheduled: true, - functionName: nameof(NotifyAggregatedMeasureDataOrchestrationV1)); + var brs_023_027_v1 = CreateBRS_023_027_V1Description(); + var brs_026_v1 = CreateBRS_026_V1Description(); - brs_023_027_v1.ParameterDefinition.SetFromType(); - - brs_023_027_v1.AppendStepDescription("Beregning"); - brs_023_027_v1.AppendStepDescription( - "Besked dannelse", - canBeSkipped: true, - skipReason: "Do not perform this step for an internal calculation."); - - return [brs_023_027_v1]; + return [brs_023_027_v1, brs_026_v1]; }); // => Handlers services.AddScoped(); @@ -68,3 +61,35 @@ await host.SynchronizeWithOrchestrationRegisterAsync("ProcessManager.Orchestrations").ConfigureAwait(false); await host.RunAsync().ConfigureAwait(false); + +OrchestrationDescription CreateBRS_023_027_V1Description() +{ + var brs023027V1 = new OrchestrationDescription( + name: "BRS_023_027", + version: 1, + canBeScheduled: true, + functionName: nameof(NotifyAggregatedMeasureDataOrchestrationV1)); + + brs023027V1.ParameterDefinition.SetFromType(); + + brs023027V1.AppendStepDescription("Beregning"); + brs023027V1.AppendStepDescription( + "Besked dannelse", + canBeSkipped: true, + skipReason: "Do not perform this step for an internal calculation."); + return brs023027V1; +} + +OrchestrationDescription CreateBRS_026_V1Description() +{ + var brs026V1 = new OrchestrationDescription( + name: "BRS_026", + version: 1, + canBeScheduled: false, + functionName: nameof(RequestCalculatedEnergyTimeSeriesOrchestrationV1)); + brs026V1.ParameterDefinition.SetFromType(); + brs026V1.AppendStepDescription("Asynkron validering"); + brs026V1.AppendStepDescription("Hent anmodningsdata"); + brs026V1.AppendStepDescription("Udsend beskeder"); + return brs026V1; +} diff --git a/source/ProcessManager.Orchestrations/local.settings.sample.json b/source/ProcessManager.Orchestrations/local.settings.sample.json index 91fc3a005f..d8f76f0abd 100644 --- a/source/ProcessManager.Orchestrations/local.settings.sample.json +++ b/source/ProcessManager.Orchestrations/local.settings.sample.json @@ -8,6 +8,9 @@ // - 'ProcessManager' (host) and 'ProcessManager.Orchestrations' (host) must use the same Task Hub "ProcessManagerStorageConnectionString": "UseDevelopmentStorage=true", "ProcessManagerTaskHubName": "Orchestrations01", - "ProcessManager__SqlDatabaseConnectionString": "" + "ProcessManager__SqlDatabaseConnectionString": "", + "ServiceBus__FullyQualifiedNamespace": "", + "ProcessManagerTopic_TopicName": "", + "ProcessManagerTopic_Brs026SubscriptionName": "" } -} \ No newline at end of file +} diff --git a/source/Shared/ProcessManager/Orchestrations/Contracts/StartOrchestrationDto.proto b/source/Shared/ProcessManager/Orchestrations/Contracts/StartOrchestrationDto.proto new file mode 100644 index 0000000000..9b1ed46602 --- /dev/null +++ b/source/Shared/ProcessManager/Orchestrations/Contracts/StartOrchestrationDto.proto @@ -0,0 +1,24 @@ +/* Copyright 2020 Energinet DataHub A/S + * + * Licensed under the Apache License, Version 2.0 (the "License2"); + * 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. + */ + +syntax = "proto3"; + +option csharp_namespace = "Energinet.DataHub.ProcessManager.Orchestrations.Contracts"; + +message StartOrchestrationDto { + string orchestration_name = 1; // The name of the orchestration to start. + int32 orchestration_version = 2; // The version of the orchestration to start. + string json_input = 3; // The input to the orchestration serialized as a JSON +} diff --git a/source/Shared/ProcessManager/Orchestrations/Processes/BRS_026/V1/Model/RequestCalculatedEnergyTimeSeriesInputV1.cs b/source/Shared/ProcessManager/Orchestrations/Processes/BRS_026/V1/Model/RequestCalculatedEnergyTimeSeriesInputV1.cs new file mode 100644 index 0000000000..bc722a42ab --- /dev/null +++ b/source/Shared/ProcessManager/Orchestrations/Processes/BRS_026/V1/Model/RequestCalculatedEnergyTimeSeriesInputV1.cs @@ -0,0 +1,18 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// 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. + +namespace Energinet.DataHub.ProcessManager.Orchestrations.Processes.BRS_026.V1.Models; + +public record RequestCalculatedEnergyTimeSeriesInputV1( + string BusinessReason);