mirror of
https://github.com/ditkrg/DIT.Workflower.git
synced 2026-01-22 22:06:42 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b653f10e3e | ||
| 4fd1e47787 | |||
| 6de3d47516 |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -4,7 +4,6 @@ updates:
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
target-branch: dev
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
@ -13,7 +12,6 @@ updates:
|
||||
# Maintain dependencies for nuget
|
||||
- package-ecosystem: "nuget"
|
||||
directory: "/"
|
||||
target-branch: dev
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
|
||||
14
README.md
14
README.md
@ -8,15 +8,15 @@ Workflower is a library based on .NET Standard, To handle Finite State Machines
|
||||
|
||||
Install the latest nuget package into your ASP.NET Core application.
|
||||
|
||||
```sh
|
||||
dotnet add package DIT.Workflower
|
||||
```
|
||||
```sh
|
||||
dotnet add package DIT.Workflower
|
||||
```
|
||||
|
||||
You can also download support for Dependency Injection:
|
||||
You can also download support for Dependency Injection:
|
||||
|
||||
```sh
|
||||
dotnet add package DIT.Workflower.DependencyInjection
|
||||
```
|
||||
```sh
|
||||
dotnet add package DIT.Workflower.DependencyInjection
|
||||
```
|
||||
|
||||
### Workflow Builder
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ public interface IWorkflow<TState, TCommand, TContext>
|
||||
/// <typeparam name="TCommand">The type of the workflow command.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the workflow context.</typeparam>
|
||||
/// <returns>A list of transition definitions.</returns>
|
||||
public List<TransitionDefinition<TState, TCommand, TContext>> GetAllTransitionDefinitions();
|
||||
public List<TransitionDefinition<TState, TCommand, ContextWrapper<TContext>>> GetAllTransitionDefinitions();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of allowed transitions without any condition checks.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
namespace DIT.Workflower.DependencyInjection;
|
||||
|
||||
public record DIContextWrapper<TContext>(TContext Context, IServiceProvider ServiceProvider);
|
||||
public record ContextWrapper<TContext>(TContext Context, IServiceProvider ServiceProvider);
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
|
||||
<!-- Deterministic Builds -->
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup >
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0" Condition="'$(TargetFramework)' == 'net6.0'" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0" Condition="'$(TargetFramework)' == 'net7.0'" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0" Condition="'$(TargetFramework)' == 'net8.0'" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" Condition="'$(TargetFramework)' == 'net6.0'" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" Condition="'$(TargetFramework)' == 'net7.0'" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" Condition="'$(TargetFramework)' == 'net8.0'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
namespace DIT.Workflower.DependencyInjection.Extensions;
|
||||
|
||||
public static class IServiceCollectionExtensions
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
|
||||
public static ITransitionStart<TState, TCommand, DIContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(this IServiceCollection services, in int version = 1)
|
||||
public static ITransitionStart<TState, TCommand, ContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(
|
||||
this IServiceCollection services, in int version = 1)
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
@ -13,25 +13,27 @@ public static class IServiceCollectionExtensions
|
||||
return AddWorkflowDefinition<TState, TCommand, TContext>(services, id, version);
|
||||
}
|
||||
|
||||
public static ITransitionStart<TState, TCommand, DIContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(this IServiceCollection services, in string id)
|
||||
public static ITransitionStart<TState, TCommand, ContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(
|
||||
this IServiceCollection services, in string id)
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
return AddWorkflowDefinition<TState, TCommand, TContext>(services, id, version: 1);
|
||||
}
|
||||
|
||||
public static ITransitionStart<TState, TCommand, DIContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(this IServiceCollection services, string id, int version)
|
||||
public static ITransitionStart<TState, TCommand, ContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(
|
||||
this IServiceCollection services, string id, int version)
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
var builder = WorkflowDefinitionBuilder<TState, TCommand, DIContextWrapper<TContext>>.Create();
|
||||
var builder = WorkflowDefinitionBuilder<TState, TCommand, ContextWrapper<TContext>>.Create();
|
||||
var definition = (WorkflowDefinitionBuilder<TState, TCommand, ContextWrapper<TContext>>)builder;
|
||||
|
||||
services.TryAddSingleton<IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>, DefaultWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>>();
|
||||
services.TryAddScoped<IWorkflowFactory<TState, TCommand, TContext>, DefaultWorkflowFactory<TState, TCommand, TContext>>();
|
||||
|
||||
services.AddSingleton<IWorkflow<TState, TCommand, DIContextWrapper<TContext>>, WorkflowDefinitionWrapper<TState, TCommand, DIContextWrapper<TContext>>>(sp =>
|
||||
services.AddScoped<IWorkflow<TState, TCommand, TContext>, WorkflowDefinitionWrapper<TState, TCommand, TContext>>(sp =>
|
||||
{
|
||||
var definition = (WorkflowDefinitionBuilder<TState, TCommand, DIContextWrapper<TContext>>)builder;
|
||||
var wrapper = new WorkflowDefinitionWrapper<TState, TCommand, DIContextWrapper<TContext>>(definition, id, version);
|
||||
var wrapper = new WorkflowDefinitionWrapper<TState, TCommand, TContext>(definition, sp, id, version);
|
||||
return wrapper;
|
||||
});
|
||||
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
namespace DIT.Workflower.DependencyInjection.Extensions;
|
||||
|
||||
public static class IServiceProviderExtensions
|
||||
public static class ServiceProviderExtensions
|
||||
{
|
||||
public static IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>> GetRequiredWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp)
|
||||
public static IWorkflowFactory<TState, TCommand, TContext> GetRequiredWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp)
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
return sp.GetRequiredService<IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>>();
|
||||
return sp.GetRequiredService<IWorkflowFactory<TState, TCommand, TContext>>();
|
||||
}
|
||||
|
||||
public static IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>? GetWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp)
|
||||
public static IWorkflowFactory<TState, TCommand, TContext>? GetWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp)
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
return sp.GetService<IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>>();
|
||||
return sp.GetService<IWorkflowFactory<TState, TCommand, TContext>>();
|
||||
}
|
||||
|
||||
public static IWorkflow<TState, TCommand, DIContextWrapper<TContext>> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, int version = 1)
|
||||
public static IWorkflow<TState, TCommand, TContext> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, int version = 1)
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
@ -24,7 +24,7 @@ public static class IServiceProviderExtensions
|
||||
return factory.CreateWorkflow(version);
|
||||
}
|
||||
|
||||
public static IWorkflow<TState, TCommand, DIContextWrapper<TContext>> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, string id, int version = 1)
|
||||
public static IWorkflow<TState, TCommand, TContext> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, string id, int version = 1)
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
|
||||
@ -1,21 +1,56 @@
|
||||
namespace DIT.Workflower.DependencyInjection;
|
||||
|
||||
public record WorkflowDefinitionWrapper<TState, TCommand, TContext> : WorkflowDefinition<TState, TCommand, TContext>, IWorkflow<TState, TCommand, TContext>
|
||||
public sealed class WorkflowDefinitionWrapper<TState, TCommand, TContext> : IWorkflow<TState, TCommand, TContext>
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly WorkflowDefinition<TState, TCommand, ContextWrapper<TContext>> _definition;
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public int Version { get; }
|
||||
|
||||
public WorkflowDefinitionWrapper(WorkflowDefinitionBuilder<TState, TCommand, TContext> builder, string id, int version)
|
||||
: base(builder.Transitions)
|
||||
public List<TransitionDefinition<TState, TCommand, ContextWrapper<TContext>>> GetAllTransitionDefinitions()
|
||||
{
|
||||
return _definition.GetAllTransitionDefinitions();
|
||||
}
|
||||
|
||||
public List<Transition<TState, TCommand>> GetAllowedTransitions(TState from)
|
||||
{
|
||||
return _definition.GetAllowedTransitions(from);
|
||||
}
|
||||
|
||||
public List<Transition<TState, TCommand>> GetAllowedTransitions(TContext context, TState from)
|
||||
{
|
||||
return _definition.GetAllowedTransitions(GetContextWrapper(context), from);
|
||||
}
|
||||
|
||||
public IEnumerable<Transition<TState, TCommand>> GetAllowedTransitions(ListTransitionsRequest<TState, TCommand, TContext> request)
|
||||
{
|
||||
var wrappedRequest = new ListTransitionsRequest<TState, TCommand, ContextWrapper<TContext>>
|
||||
{
|
||||
From = request.From,
|
||||
To = request.To,
|
||||
Command = request.Command,
|
||||
Context = request.Context is null ? null : GetContextWrapper(request.Context)
|
||||
};
|
||||
|
||||
return _definition.GetAllowedTransitions(wrappedRequest);
|
||||
}
|
||||
|
||||
public WorkflowDefinitionWrapper(WorkflowDefinitionBuilder<TState, TCommand, ContextWrapper<TContext>> builder, IServiceProvider serviceProvider, string id, int version)
|
||||
{
|
||||
_definition = builder.Build();
|
||||
_serviceProvider = serviceProvider;
|
||||
|
||||
Id = id;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
private ContextWrapper<TContext> GetContextWrapper(in TContext context)
|
||||
=> new(context, _serviceProvider);
|
||||
|
||||
public static string GetDefaultId()
|
||||
{
|
||||
var ctxType = typeof(TContext);
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
<!-- Deterministic Builds -->
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
namespace DIT.Workflower;
|
||||
|
||||
public record WorkflowDefinition<TState, TCommand, TContext>
|
||||
public class WorkflowDefinition<TState, TCommand, TContext>
|
||||
where TState : struct
|
||||
where TCommand : struct
|
||||
{
|
||||
@ -41,24 +41,24 @@ public record WorkflowDefinition<TState, TCommand, TContext>
|
||||
{
|
||||
var query = _transitions.AsQueryable();
|
||||
|
||||
if (request.From is TState from)
|
||||
if (request.From is { } from)
|
||||
{
|
||||
query = query.Where(doc => doc.From.Equals(from));
|
||||
}
|
||||
|
||||
if (request.To is TState to)
|
||||
if (request.To is { } to)
|
||||
{
|
||||
query = query.Where(doc => doc.To.Equals(to));
|
||||
}
|
||||
|
||||
if (request.Command is TCommand command)
|
||||
if (request.Command is { } command)
|
||||
{
|
||||
query = query.Where(doc => doc.Command.Equals(command));
|
||||
}
|
||||
|
||||
if (request.Context is not null)
|
||||
{
|
||||
query = query.Where(doc => doc.Conditions == null || !doc.Conditions.Any(cond => !cond(request.Context)));
|
||||
query = query.Where(doc => doc.Conditions == null || doc.Conditions.All(cond => cond(request.Context)));
|
||||
}
|
||||
|
||||
return query.Select(x => x.ToTransition());
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using DIT.Workflower.DependencyInjection.Abstractions;
|
||||
using DIT.Workflower.DependencyInjection.Extensions;
|
||||
using DIT.Workflower.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DIT.Workflower.Tests.DependencyInjection;
|
||||
@ -49,7 +48,6 @@ public class DependencyInjectionTests
|
||||
Assert.NotNull(v1);
|
||||
Assert.NotNull(v2);
|
||||
|
||||
|
||||
Assert.Single(v1!.GetAllowedTransitions(PhoneState.Idle));
|
||||
Assert.Single(v2!.GetAllowedTransitions(PhoneState.Idle));
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user