Compare commits

..

No commits in common. "main" and "0.1.2" have entirely different histories.
main ... 0.1.2

17 changed files with 89 additions and 240 deletions

View File

@ -3,13 +3,13 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"dotnet-sonarscanner": { "dotnet-sonarscanner": {
"version": "6.2.0", "version": "5.7.2",
"commands": [ "commands": [
"dotnet-sonarscanner" "dotnet-sonarscanner"
] ]
}, },
"dotnet-coverage": { "dotnet-coverage": {
"version": "17.11.0", "version": "17.3.2",
"commands": [ "commands": [
"dotnet-coverage" "dotnet-coverage"
] ]

View File

@ -4,6 +4,7 @@ updates:
# Maintain dependencies for GitHub Actions # Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
target-branch: dev
schedule: schedule:
interval: "daily" interval: "daily"
reviewers: reviewers:
@ -12,7 +13,8 @@ updates:
# Maintain dependencies for nuget # Maintain dependencies for nuget
- package-ecosystem: "nuget" - package-ecosystem: "nuget"
directory: "/" directory: "/"
target-branch: dev
schedule: schedule:
interval: "daily" interval: "daily"
reviewers: reviewers:
- "ditkrg/dotnet" - "ditkrg/digital-development-net"

View File

@ -1,4 +1,3 @@
---
name: Deploy To Nuget name: Deploy To Nuget
on: on:
@ -20,13 +19,13 @@ jobs:
build-push: build-push:
name: Build and Publish name: Build and Publish
needs: [test] needs: [test]
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v4 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: "8.0.x" dotnet-version: "6.0.x"
- name: Restore packages - name: Restore packages
run: dotnet restore run: dotnet restore

View File

@ -1,4 +1,3 @@
---
name: Run SonarQube Analysis name: Run SonarQube Analysis
on: on:
@ -14,10 +13,39 @@ concurrency:
group: sonarqube-analysis group: sonarqube-analysis
jobs: jobs:
test: run-tests:
uses: ./.github/workflows/tests-base.yaml name: Run SonarQube Analysis
with: timeout-minutes: 10
sonarqube: true runs-on: ubuntu-latest
sonarqube_host: ${{ vars.SONARQUBE_HOST }}
secrets: env:
PROJECT_KEY: ditkrg_DIT.Workflower_AYF14rjSb80e2b0bns3t
SONARQUBE_HOST: ${{ secrets.SONARQUBE_HOST }}
SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
ASPNETCORE_ENVIRONMENT: Testing
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "11"
- name: Restore tools
run: dotnet tool restore
- name: Run Tests (SonarQube)
run: |
dotnet tool run dotnet-sonarscanner begin -k:"$PROJECT_KEY" \
-d:sonar.login="$SONARQUBE_TOKEN" \
-d:sonar.host.url="$SONARQUBE_HOST" \
-d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml
dotnet build --no-incremental
dotnet dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml"
dotnet tool run dotnet-sonarscanner end -d:sonar.login="$SONARQUBE_TOKEN"

View File

@ -12,19 +12,6 @@ on:
- "!.github/workflows/tests-base.yaml" - "!.github/workflows/tests-base.yaml"
workflow_call: workflow_call:
inputs:
sonarqube:
type: boolean
required: false
default: false
sonarqube_host:
type: string
required: false
secrets:
SONARQUBE_TOKEN:
required: false
jobs: jobs:
run-tests: run-tests:
@ -36,49 +23,11 @@ jobs:
ASPNETCORE_ENVIRONMENT: Testing ASPNETCORE_ENVIRONMENT: Testing
steps: steps:
- uses: actions/setup-dotnet@v4 - uses: actions/checkout@v3
with:
dotnet-version: "8.0.x"
- uses: actions/checkout@v4 - uses: actions/setup-dotnet@v2
if: ${{ !inputs.sonarqube }} with:
dotnet-version: "6.0.x"
- name: Run tests - name: Run tests
if: ${{ !inputs.sonarqube }}
run: dotnet test run: dotnet test
###############################
########## SONARQUBE ##########
###############################
- name: Set up JDK
uses: actions/setup-java@v4
if: ${{ inputs.sonarqube }}
with:
distribution: "zulu"
java-version: "17"
- uses: actions/checkout@v4
if: ${{ inputs.sonarqube }}
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Restore tools
if: ${{ inputs.sonarqube }}
run: dotnet tool restore
- name: Run tests (SonarQube)
if: ${{ inputs.sonarqube }}
env:
PROJECT_KEY: ditkrg_DIT.Workflower_AYF14rjSb80e2b0bns3t
SONARQUBE_HOST: ${{ inputs.sonarqube_host }}
SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
run: |
dotnet tool run dotnet-sonarscanner begin -k:"$PROJECT_KEY" \
-d:sonar.login="$SONARQUBE_TOKEN" \
-d:sonar.host.url="$SONARQUBE_HOST" \
-d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml
dotnet build --no-incremental
dotnet dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml"
dotnet tool run dotnet-sonarscanner end -d:sonar.login="$SONARQUBE_TOKEN"

View File

@ -10,15 +10,6 @@ public interface IWorkflow<TState, TCommand, TContext>
string Reference => $"{Id}.v{Version}"; string Reference => $"{Id}.v{Version}";
/// <summary>
/// Retrieves all transition definitions for the workflow.
/// </summary>
/// <typeparam name="TState">The type of the workflow state.</typeparam>
/// <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, ContextWrapper<TContext>>> GetAllTransitionDefinitions();
/// <summary> /// <summary>
/// Gets a list of allowed transitions without any condition checks. /// Gets a list of allowed transitions without any condition checks.
/// </summary> /// </summary>
@ -33,13 +24,4 @@ public interface IWorkflow<TState, TCommand, TContext>
/// <param name="from">The incoming state</param> /// <param name="from">The incoming state</param>
/// <returns>A list of available transitions for the current context</returns> /// <returns>A list of available transitions for the current context</returns>
public List<Transition<TState, TCommand>> GetAllowedTransitions(TContext context, TState from); public List<Transition<TState, TCommand>> GetAllowedTransitions(TContext context, TState from);
/// <summary>
/// Retrieves a list of allowed transitions based on the provided request.
/// </summary>
/// <typeparam name="TState">The type of the workflow state.</typeparam>
/// <typeparam name="TCommand">The type of the workflow command.</typeparam>
/// <param name="request">The request object containing the necessary information.</param>
/// <returns>An enumerable of allowed transitions.</returns>
public IEnumerable<Transition<TState, TCommand>> GetAllowedTransitions(ListTransitionsRequest<TState, TCommand, TContext> request);
} }

View File

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

View File

@ -1,29 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<PackageId>DIT.Workflower.DependencyInjection</PackageId>
<Authors>DIT</Authors>
<Company>DIT</Company>
<!-- Symbols for nuget -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<!-- Deterministic Builds -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" Condition="'$(TargetFramework)' == 'net6.0'" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.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>
<ItemGroup> <ItemGroup>

View File

@ -2,10 +2,10 @@
namespace DIT.Workflower.DependencyInjection.Extensions; namespace DIT.Workflower.DependencyInjection.Extensions;
public static class ServiceCollectionExtensions public static class IServiceCollectionExtensions
{ {
public static ITransitionStart<TState, TCommand, ContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(
this IServiceCollection services, in int version = 1) public static ITransitionStart<TState, TCommand, DIContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(this IServiceCollection services, in int version = 1)
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
@ -13,27 +13,25 @@ public static class ServiceCollectionExtensions
return AddWorkflowDefinition<TState, TCommand, TContext>(services, id, version); return AddWorkflowDefinition<TState, TCommand, TContext>(services, id, version);
} }
public static ITransitionStart<TState, TCommand, ContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>( public static ITransitionStart<TState, TCommand, DIContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(this IServiceCollection services, in string id)
this IServiceCollection services, in string id)
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
return AddWorkflowDefinition<TState, TCommand, TContext>(services, id, version: 1); return AddWorkflowDefinition<TState, TCommand, TContext>(services, id, version: 1);
} }
public static ITransitionStart<TState, TCommand, ContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>( public static ITransitionStart<TState, TCommand, DIContextWrapper<TContext>> AddWorkflowDefinition<TState, TCommand, TContext>(this IServiceCollection services, string id, int version)
this IServiceCollection services, string id, int version)
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
var builder = WorkflowDefinitionBuilder<TState, TCommand, ContextWrapper<TContext>>.Create(); var builder = WorkflowDefinitionBuilder<TState, TCommand, DIContextWrapper<TContext>>.Create();
var definition = (WorkflowDefinitionBuilder<TState, TCommand, ContextWrapper<TContext>>)builder;
services.TryAddScoped<IWorkflowFactory<TState, TCommand, TContext>, DefaultWorkflowFactory<TState, TCommand, TContext>>(); services.TryAddSingleton<IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>, DefaultWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>>();
services.AddScoped<IWorkflow<TState, TCommand, TContext>, WorkflowDefinitionWrapper<TState, TCommand, TContext>>(sp => services.AddSingleton<IWorkflow<TState, TCommand, DIContextWrapper<TContext>>, WorkflowDefinitionWrapper<TState, TCommand, DIContextWrapper<TContext>>>(sp =>
{ {
var wrapper = new WorkflowDefinitionWrapper<TState, TCommand, TContext>(definition, sp, id, version); var definition = (WorkflowDefinitionBuilder<TState, TCommand, DIContextWrapper<TContext>>)builder;
var wrapper = new WorkflowDefinitionWrapper<TState, TCommand, DIContextWrapper<TContext>>(definition, id, version);
return wrapper; return wrapper;
}); });

View File

@ -1,22 +1,22 @@
namespace DIT.Workflower.DependencyInjection.Extensions; namespace DIT.Workflower.DependencyInjection.Extensions;
public static class ServiceProviderExtensions public static class IServiceProviderExtensions
{ {
public static IWorkflowFactory<TState, TCommand, TContext> GetRequiredWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp) public static IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>> GetRequiredWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp)
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
return sp.GetRequiredService<IWorkflowFactory<TState, TCommand, TContext>>(); return sp.GetRequiredService<IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>>();
} }
public static IWorkflowFactory<TState, TCommand, TContext>? GetWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp) public static IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>? GetWorkflowFactory<TState, TCommand, TContext>(this IServiceProvider sp)
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
return sp.GetService<IWorkflowFactory<TState, TCommand, TContext>>(); return sp.GetService<IWorkflowFactory<TState, TCommand, DIContextWrapper<TContext>>>();
} }
public static IWorkflow<TState, TCommand, TContext> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, int version = 1) public static IWorkflow<TState, TCommand, DIContextWrapper<TContext>> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, int version = 1)
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
@ -24,7 +24,7 @@ public static class ServiceProviderExtensions
return factory.CreateWorkflow(version); return factory.CreateWorkflow(version);
} }
public static IWorkflow<TState, TCommand, TContext> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, string id, int version = 1) public static IWorkflow<TState, TCommand, DIContextWrapper<TContext>> CreateWorkflow<TState, TCommand, TContext>(this IServiceProvider sp, string id, int version = 1)
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {

View File

@ -1,56 +1,21 @@
namespace DIT.Workflower.DependencyInjection; namespace DIT.Workflower.DependencyInjection;
public sealed class WorkflowDefinitionWrapper<TState, TCommand, TContext> : IWorkflow<TState, TCommand, TContext> public record WorkflowDefinitionWrapper<TState, TCommand, TContext> : WorkflowDefinition<TState, TCommand, TContext>, IWorkflow<TState, TCommand, TContext>
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
private readonly IServiceProvider _serviceProvider;
private readonly WorkflowDefinition<TState, TCommand, ContextWrapper<TContext>> _definition;
public string Id { get; } public string Id { get; }
public int Version { get; } public int Version { get; }
public List<TransitionDefinition<TState, TCommand, ContextWrapper<TContext>>> GetAllTransitionDefinitions()
{
return _definition.GetAllTransitionDefinitions();
}
public List<Transition<TState, TCommand>> GetAllowedTransitions(TState from) public WorkflowDefinitionWrapper(WorkflowDefinitionBuilder<TState, TCommand, TContext> builder, string id, int version)
: base(builder.Transitions)
{ {
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; Id = id;
Version = version; Version = version;
} }
private ContextWrapper<TContext> GetContextWrapper(in TContext context)
=> new(context, _serviceProvider);
public static string GetDefaultId() public static string GetDefaultId()
{ {
var ctxType = typeof(TContext); var ctxType = typeof(TContext);

View File

@ -5,20 +5,6 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<IsPackable>true</IsPackable>
<PackageId>DIT.Workflower</PackageId>
<Authors>DIT</Authors>
<Company>DIT</Company>
<!-- Symbols for nuget -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<!-- Deterministic Builds -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -1,15 +0,0 @@
namespace DIT.Workflower;
public sealed class ListTransitionsRequest<TState, TCommand, TContext>
where TState : struct
where TCommand : struct
{
public TState? From { get; set; }
public TState? To { get; set; }
public TCommand? Command { get; set; }
public TContext? Context { get; set; }
}

View File

@ -1,6 +1,6 @@
namespace DIT.Workflower; namespace DIT.Workflower;
public class WorkflowDefinition<TState, TCommand, TContext> public record WorkflowDefinition<TState, TCommand, TContext>
where TState : struct where TState : struct
where TCommand : struct where TCommand : struct
{ {
@ -12,8 +12,6 @@ public class WorkflowDefinition<TState, TCommand, TContext>
_transitions = transitions; _transitions = transitions;
} }
public List<TransitionDefinition<TState, TCommand, TContext>> GetAllTransitionDefinitions() => _transitions;
/// <summary> /// <summary>
/// Lists all allowed transitions from current state for the given context. /// Lists all allowed transitions from current state for the given context.
/// </summary> /// </summary>
@ -35,33 +33,4 @@ public class WorkflowDefinition<TState, TCommand, TContext>
return query.Select(x => x.ToTransition()).ToList(); return query.Select(x => x.ToTransition()).ToList();
} }
public IEnumerable<Transition<TState, TCommand>> GetAllowedTransitions(ListTransitionsRequest<TState, TCommand, TContext> request)
{
var query = _transitions.AsQueryable();
if (request.From is { } from)
{
query = query.Where(doc => doc.From.Equals(from));
}
if (request.To is { } to)
{
query = query.Where(doc => doc.To.Equals(to));
}
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.All(cond => cond(request.Context)));
}
return query.Select(x => x.ToTransition());
}
} }

View File

@ -9,14 +9,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0"> <PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -1,4 +1,5 @@
using DIT.Workflower.DependencyInjection.Extensions; using DIT.Workflower.DependencyInjection.Abstractions;
using DIT.Workflower.DependencyInjection.Extensions;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace DIT.Workflower.Tests.DependencyInjection; namespace DIT.Workflower.Tests.DependencyInjection;
@ -48,6 +49,7 @@ public class DependencyInjectionTests
Assert.NotNull(v1); Assert.NotNull(v1);
Assert.NotNull(v2); Assert.NotNull(v2);
Assert.Single(v1!.GetAllowedTransitions(PhoneState.Idle)); Assert.Single(v1!.GetAllowedTransitions(PhoneState.Idle));
Assert.Single(v2!.GetAllowedTransitions(PhoneState.Idle)); Assert.Single(v2!.GetAllowedTransitions(PhoneState.Idle));