Compare commits

...

3 Commits
0.2.1 ... main

Author SHA1 Message Date
Brusk Awat
b653f10e3e
Update README.md 2024-08-16 00:42:53 +03:00
4fd1e47787
Remove target branch from dependabot 2024-08-14 13:04:17 +03:00
6de3d47516
BREAKING: Use Scoped services for the IWorkflow in DI. 2024-08-14 12:56:39 +03:00
11 changed files with 78 additions and 45 deletions

View File

@ -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:

View File

@ -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

View File

@ -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.

View File

@ -1,3 +1,3 @@
namespace DIT.Workflower.DependencyInjection;
public record DIContextWrapper<TContext>(TContext Context, IServiceProvider ServiceProvider);
public record ContextWrapper<TContext>(TContext Context, IServiceProvider ServiceProvider);

View File

@ -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>

View File

@ -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;
});

View File

@ -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
{

View File

@ -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);

View File

@ -17,7 +17,7 @@
<!-- Deterministic Builds -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>

View File

@ -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());

View File

@ -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));