Incremental work on Environments module

This commit is contained in:
Sipke Schoorstra 2023-05-16 10:03:55 +02:00
parent 1bc3e3b959
commit e1ec2c87d0
15 changed files with 275 additions and 1 deletions

View file

@ -198,6 +198,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.ProtoAc
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.HttpEndpoints", "src\samples\aspnet\Elsa.Samples.AspNet.HttpEndpoints\Elsa.Samples.AspNet.HttpEndpoints.csproj", "{165ACC9D-B67C-4B49-A818-ECFD247C899C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Environments", "src\modules\Elsa.Environments\Elsa.Environments.csproj", "{B5CDF747-8066-40D5-9BAE-CBE397BC9B51}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -504,6 +506,10 @@ Global
{165ACC9D-B67C-4B49-A818-ECFD247C899C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{165ACC9D-B67C-4B49-A818-ECFD247C899C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{165ACC9D-B67C-4B49-A818-ECFD247C899C}.Release|Any CPU.Build.0 = Release|Any CPU
{B5CDF747-8066-40D5-9BAE-CBE397BC9B51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5CDF747-8066-40D5-9BAE-CBE397BC9B51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5CDF747-8066-40D5-9BAE-CBE397BC9B51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5CDF747-8066-40D5-9BAE-CBE397BC9B51}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{155227F0-A33B-40AA-A4B4-06F813EB921B} = {61017E64-6D00-49CB-9E81-5002DC8F7D5F}
@ -592,5 +598,6 @@ Global
{044C3108-FE79-460A-9C31-A03C30228836} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
{3212A999-4AC4-4911-9AA4-92AB906BCB5E} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
{165ACC9D-B67C-4B49-A818-ECFD247C899C} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
{B5CDF747-8066-40D5-9BAE-CBE397BC9B51} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
EndGlobalSection
EndGlobal

View file

@ -14,6 +14,7 @@
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.PostgreSql\Elsa.EntityFrameworkCore.PostgreSql.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.Sqlite\Elsa.EntityFrameworkCore.Sqlite.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore\Elsa.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Environments\Elsa.Environments.csproj" />
<ProjectReference Include="..\..\modules\Elsa.MassTransit\Elsa.MassTransit.csproj" />
<ProjectReference Include="..\..\modules\Elsa.ProtoActor\Elsa.ProtoActor.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Identity\Elsa.Identity.csproj" />

View file

@ -42,7 +42,7 @@ services
runtime.UseAsyncWorkflowStateExporter();
runtime.UseMassTransitDispatcher();
})
.UseLabels(labels => labels.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
.UseEnvironments(environments => environments.EnvironmentsOptions = options => configuration.GetSection("Environments").Bind(options))
.UseScheduling()
.UseWorkflowsApi(api => api.AddFastEndpointsAssembly<Program>())
.UseJavaScript()

View file

@ -61,5 +61,21 @@
"hashedClientSecretSalt": "xRKy14Ok1/tU3kLf/8V1fcbLIegy9vcM90Peu2tzohU="
}
]
},
"Environments": {
"Environments": [
{
"Name": "Local",
"ServerUrl": "https://localhost:5001/elsa/api"
},
{
"Name": "Staging",
"ServerUrl": "https://staging.acme.com/elsa/api"
},
{
"Name": "Production",
"ServerUrl": "https://production.acme.com/elsa/api"
}
]
}
}

View file

@ -0,0 +1,16 @@
using Elsa.Environments.Models;
namespace Elsa.Environments.Contracts;
/// <summary>
/// Manages environments.
/// </summary>
public interface IEnvironmentsManager
{
/// <summary>
/// Returns all environments.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>All environments.</returns>
Task<IEnumerable<WorkflowsEnvironment>> ListEnvironmentsAsync(CancellationToken cancellationToken = default);
}

View file

@ -0,0 +1,16 @@
using Elsa.Environments.Models;
namespace Elsa.Environments.Contracts;
/// <summary>
/// Provides environments to the system.
/// </summary>
public interface IEnvironmentsProvider
{
/// <summary>
/// Returns a list of workflow environments.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of workflow environments.</returns>
ValueTask<IEnumerable<WorkflowsEnvironment>> GetEnvironmentsAsync(CancellationToken cancellationToken = default);
}

View file

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<Import Project="..\..\..\configureawait.props" />
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>3.0.0</Version>
<Description>
Provides API endpoints for client applications to enumerate available environments.
</Description>
<PackageTags>elsa workflows environments api</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\Elsa.Api.Common\Elsa.Api.Common.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,32 @@
using Elsa.Abstractions;
using Elsa.Environments.Contracts;
using Elsa.Environments.Models;
using JetBrains.Annotations;
namespace Elsa.Environments.Endpoints.Environments.List;
[PublicAPI]
internal class Endpoint : ElsaEndpointWithoutRequest<Response>
{
private readonly IEnvironmentsManager _environmentsManager;
public Endpoint(IEnvironmentsManager environmentsManager)
{
_environmentsManager = environmentsManager;
}
public override void Configure()
{
Get("/environments");
ConfigurePermissions("read:environments");
}
public override async Task<Response> ExecuteAsync(CancellationToken cancellationToken)
{
var environments = await _environmentsManager.ListEnvironmentsAsync(cancellationToken);
return new(environments.ToList());
}
}
[PublicAPI]
internal record Response(ICollection<WorkflowsEnvironment> Environments);

View file

@ -0,0 +1,25 @@
using Elsa.Environments.Features;
using Elsa.Features.Services;
using JetBrains.Annotations;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Extension methods for <see cref="IModule"/> to add environment services.
/// </summary>
[PublicAPI]
public static class ModuleExtensions
{
/// <summary>
/// Adds support for environments.
/// </summary>
/// <param name="module">The module.</param>
/// <param name="configure">The configuration delegate.</param>
/// <returns>The module.</returns>
public static IModule UseEnvironments(this IModule module, Action<EnvironmentsFeature>? configure = default)
{
module.Use(configure);
return module;
}
}

View file

@ -0,0 +1,43 @@
using Elsa.Environments.Contracts;
using Elsa.Environments.Options;
using Elsa.Environments.Providers;
using Elsa.Environments.Services;
using Elsa.Extensions;
using Elsa.Features.Abstractions;
using Elsa.Features.Services;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Environments.Features;
/// <summary>
/// Installs the <see cref="EnvironmentsFeature"/> feature.
/// </summary>
[PublicAPI]
public class EnvironmentsFeature : FeatureBase
{
/// <inheritdoc />
public EnvironmentsFeature(IModule module) : base(module)
{
}
/// <summary>
/// A delegate to configure the <see cref="EnvironmentsOptions"/>.
/// </summary>
public Action<EnvironmentsOptions> EnvironmentsOptions { get; set; } = _ => { };
/// <inheritdoc />
public override void Configure()
{
Module.AddFastEndpointsAssembly(GetType());
}
/// <inheritdoc />
public override void Apply()
{
Services.Configure(EnvironmentsOptions);
Services.AddSingleton<IEnvironmentsProvider, ConfigurationEnvironmentsProvider>();
Services.AddSingleton<IEnvironmentsManager, DefaultEnvironmentsManager>();
}
}

View file

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait />
</Weavers>

View file

@ -0,0 +1,22 @@
namespace Elsa.Environments.Models;
/// <summary>
/// Represents the environment in which the workflow engine is running.
/// </summary>
public class WorkflowsEnvironment
{
/// <summary>
/// The name of the environment.
/// </summary>
public string Name { get; set; } = default!;
/// <summary>
/// The URL of the server hosting the workflow engine.
/// </summary>
public Uri ServerUrl { get; set; } = default!;
/// <summary>
/// A dictionary of custom properties.
/// </summary>
public IDictionary<string, object> CustomProperties { get; set; } = new Dictionary<string, object>();
}

View file

@ -0,0 +1,14 @@
using Elsa.Environments.Models;
namespace Elsa.Environments.Options;
/// <summary>
/// EnvironmentsOptions for providing environments.
/// </summary>
public class EnvironmentsOptions
{
/// <summary>
/// Gets or sets a list of environments.
/// </summary>
public ICollection<WorkflowsEnvironment> Environments { get; set; } = new List<WorkflowsEnvironment>();
}

View file

@ -0,0 +1,28 @@
using Elsa.Environments.Contracts;
using Elsa.Environments.Models;
using Elsa.Environments.Options;
using Microsoft.Extensions.Options;
namespace Elsa.Environments.Providers;
/// <summary>
/// An implementation of <see cref="IEnvironmentsProvider"/> that reads environment definitions from configuration.
/// </summary>
public class ConfigurationEnvironmentsProvider : IEnvironmentsProvider
{
private readonly IOptions<EnvironmentsOptions> _options;
/// <summary>
/// Initializes a new instance of <see cref="ConfigurationEnvironmentsProvider"/>.
/// </summary>
public ConfigurationEnvironmentsProvider(IOptions<EnvironmentsOptions> options)
{
_options = options;
}
/// <inheritdoc />
public ValueTask<IEnumerable<WorkflowsEnvironment>> GetEnvironmentsAsync(CancellationToken cancellationToken = default)
{
return new (_options.Value.Environments);
}
}

View file

@ -0,0 +1,32 @@
using Elsa.Environments.Contracts;
using Elsa.Environments.Models;
namespace Elsa.Environments.Services;
/// <inheritdoc />
public class DefaultEnvironmentsManager : IEnvironmentsManager
{
private readonly IEnumerable<IEnvironmentsProvider> _providers;
/// <summary>
/// Initializes a new instance of <see cref="DefaultEnvironmentsManager"/>.
/// </summary>
public DefaultEnvironmentsManager(IEnumerable<IEnvironmentsProvider> providers)
{
_providers = providers;
}
/// <inheritdoc />
public async Task<IEnumerable<WorkflowsEnvironment>> ListEnvironmentsAsync(CancellationToken cancellationToken = default)
{
var allEnvironments = new List<WorkflowsEnvironment>();
foreach (var provider in _providers)
{
var environments = await provider.GetEnvironmentsAsync(cancellationToken);
allEnvironments.AddRange(environments);
}
return allEnvironments;
}
}