Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workflow visualization #54

Merged
merged 12 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ on:
env:
PROJECT_NAME: 'PowerPipe'
PROJECT_PATH: 'src/PowerPipe/PowerPipe.csproj'

PROJECT_EXTENSIONS_NAME: 'PowerPipe.Extensions.MicrosoftDependencyInjection'
PROJECT_EXTENSIONS_PATH: 'src/PowerPipe.Extensions.MicrosoftDependencyInjection/PowerPipe.Extensions.MicrosoftDependencyInjection.csproj'

PROJECT_VISUALIZATION_NAME: 'PowerPipe.Visualization'
PROJECT_VISUALIZATION_PATH: 'src/PowerPipe.Visualization/PowerPipe.Visualization.csproj'

PROJECT_VISUALIZATION_EXTENSIONS_NAME: 'PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection'
PROJECT_VISUALIZATION_EXTENSIONS_PATH: 'src/PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection/PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection.csproj'

PACKAGE_OUTPUT_DIRECTORY: ${{ github.workspace }}\output
NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json'

Expand Down Expand Up @@ -45,4 +53,16 @@ jobs:
run: |
dotnet build ${{ env.PROJECT_EXTENSIONS_PATH }} --no-restore --configuration Release
dotnet pack ${{ env.PROJECT_EXTENSIONS_PATH }} --no-restore --no-build --configuration Release --include-symbols -p:PackageVersion=${{ steps.version.outputs.full_without_prefix }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }}
dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}\${{ env.PROJECT_EXTENSIONS_NAME }}.${{ steps.version.outputs.full_without_prefix }}.nupkg -k ${{ secrets.NUGET_API_KEY }} -s ${{ env.NUGET_SOURCE_URL }}
dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}\${{ env.PROJECT_EXTENSIONS_NAME }}.${{ steps.version.outputs.full_without_prefix }}.nupkg -k ${{ secrets.NUGET_API_KEY }} -s ${{ env.NUGET_SOURCE_URL }}

- name: Build&Publish PowerPipe.Visualization
run: |
dotnet build ${{ env.PROJECT_VISUALIZATION_PATH }} --no-restore --configuration Release
dotnet pack ${{ env.PROJECT_VISUALIZATION_PATH }} --no-restore --no-build --configuration Release --include-symbols -p:PackageVersion=${{ steps.version.outputs.full_without_prefix }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }}
dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}\${{ env.PROJECT_VISUALIZATION_NAME }}.${{ steps.version.outputs.full_without_prefix }}.nupkg -k ${{ secrets.NUGET_API_KEY }} -s ${{ env.NUGET_SOURCE_URL }}

- name: Build&Publish PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection
run: |
dotnet build ${{ env.PROJECT_VISUALIZATION_EXTENSIONS_PATH }} --no-restore --configuration Release
dotnet pack ${{ env.PROJECT_VISUALIZATION_EXTENSIONS_PATH }} --no-restore --no-build --configuration Release --include-symbols -p:PackageVersion=${{ steps.version.outputs.full_without_prefix }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }}
dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}\${{ env.PROJECT_VISUALIZATION_EXTENSIONS_NAME }}.${{ steps.version.outputs.full_without_prefix }}.nupkg -k ${{ secrets.NUGET_API_KEY }} -s ${{ env.NUGET_SOURCE_URL }}
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>

<PackageIcon>logo.png</PackageIcon>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

Expand Down
15 changes: 15 additions & 0 deletions PowerPipe.sln
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{5A41
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerPipe.Sample", "samples\PowerPipe.Sample\PowerPipe.Sample.csproj", "{931462F7-C5FE-4A73-9F2C-75D79AD6F9F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerPipe.Visualization", "src\PowerPipe.Visualization\PowerPipe.Visualization.csproj", "{2AB1CB82-0B84-494B-A391-4D1EB73B52D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection", "src\PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection\PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection.csproj", "{50C7988C-E66C-410D-8B09-35FD707D5A7C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CFF6E12F-B083-428E-990C-D1221F97D7D0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -32,8 +38,17 @@ Global
{931462F7-C5FE-4A73-9F2C-75D79AD6F9F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{931462F7-C5FE-4A73-9F2C-75D79AD6F9F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{931462F7-C5FE-4A73-9F2C-75D79AD6F9F0}.Release|Any CPU.Build.0 = Release|Any CPU
{2AB1CB82-0B84-494B-A391-4D1EB73B52D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AB1CB82-0B84-494B-A391-4D1EB73B52D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2AB1CB82-0B84-494B-A391-4D1EB73B52D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AB1CB82-0B84-494B-A391-4D1EB73B52D5}.Release|Any CPU.Build.0 = Release|Any CPU
{50C7988C-E66C-410D-8B09-35FD707D5A7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50C7988C-E66C-410D-8B09-35FD707D5A7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50C7988C-E66C-410D-8B09-35FD707D5A7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50C7988C-E66C-410D-8B09-35FD707D5A7C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{931462F7-C5FE-4A73-9F2C-75D79AD6F9F0} = {5A41270C-1028-4CDE-B6B9-F867B1FBB613}
{7307FC89-968C-4769-B1A3-C3C2F7B439A8} = {CFF6E12F-B083-428E-990C-D1221F97D7D0}
EndGlobalSection
EndGlobal
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ If you like this project give it a star 🌟

- Lightweight
- Fluent interface
- Conditional steps
- Parallel steps execution
- Nested pipelines
- Error handling
- Steps compensation
- Ease & Structured Workflow construction
- Dependency Injection support
- Developed using .NET 6
- Developed using latest .NET

## 🧐 Sample use case

Expand Down Expand Up @@ -62,6 +67,50 @@ public class ECommercePipelineService : IECommercePipelineService
}
```

## 🤩 Workflow visualization

Sometimes workflows could be too big to track what is happening.

That's why we created a workflow visualization tool. This tool was designed with simplicity in mind.
You have to add just **few lines of code** to make this work!

### Install required packages

- Package Manager Console
```
Install-Package PowerPipe.Visualization
Install-Package PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection
```

- .NET CLI
```
dotnet add package PowerPipe.Visualization
dotnet add package PowerPipe.Visualization.Extensions.MicrosoftDependencyInjection
```

### Usage

In your Startup/Program file add required services and register middleware:

``` csharp
builder.Services.AddPowerPipeVisualization(o => o.ScanFromType(typeof(ECommercePipelineService)));

// ...

app.UsePowerPipeVisualization();
```

Then start you application and navigate to `/powerpipe` endpoint.

And workflow from the sample above parsed to this beautiful diagram. 🌟

<img src="https://github.com/mvSapphire/PowerPipe/blob/master/assets/readme-diagram-sample.png?raw=true" alt="drawing" width="800"/>

> Note! This is the very first version of workflow visualization! Looking forward to your feedback 🤗

> Known issues:
> - OnError parsing could lead to missing steps on the diagram

## 🛠️ Getting started

### Installation
Expand Down
6 changes: 4 additions & 2 deletions samples/PowerPipe.Sample/SamplePipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class SamplePipeline
{
private readonly IPipelineStepFactory _pipelineStepFactory;

private bool SampleParallelStep1ExecutionAllowed(SamplePipelineContext context) => false;
private bool SampleParallelStep6ExecutionAllowed(SamplePipelineContext context) => false;
private bool Step2ExecutionAllowed(SamplePipelineContext context) => true;
private bool Step3ExecutionAllowed(SamplePipelineContext context) => false;
private bool NestedPipelineExecutionAllowed(SamplePipelineContext context) => true;
Expand All @@ -24,15 +26,15 @@ public IPipeline<SamplePipelineResult> SetupPipeline()

var builder = new PipelineBuilder<SamplePipelineContext, SamplePipelineResult>(_pipelineStepFactory, context)
.Parallel(b => b
.AddIf<SampleParallelStep1>(_ => false)
.AddIf<SampleParallelStep1>(SampleParallelStep1ExecutionAllowed)
.Add<SampleParallelStep2>()
.Add<SampleParallelStep3>()
.Add<SampleParallelStep4>()
.OnError(PipelineStepErrorHandling.Suppress)
.Add<SampleParallelStep5>()
.OnError(PipelineStepErrorHandling.Retry)
.CompensateWith<SampleParallelStep5Compensation>()
.AddIfElse<SampleParallelStep6, SampleParallelStep7>(_ => false))
.AddIfElse<SampleParallelStep6, SampleParallelStep7>(SampleParallelStep6ExecutionAllowed))
.Add<SampleStep1>()
.CompensateWith<SampleStep1Compensation>()
.AddIf<SampleStep2>(Step2ExecutionAllowed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using PowerPipe.Visualization;

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Extension methods for configuring PowerPipe visualization middleware.
/// </summary>
public static class ApplicationBuilderExtension
{
/// <summary>
/// Adds PowerPipe visualization middleware to the application's request pipeline.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> instance.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance for method chaining.</returns>
public static IApplicationBuilder UsePowerPipeVisualization(this IApplicationBuilder app) =>
app.UseMiddleware<PipelineVisualizationMiddleware>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<IsPackable>true</IsPackable>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PowerPipe.Visualization\PowerPipe.Visualization.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using Microsoft.Extensions.Options;
using PowerPipe.Visualization;
using PowerPipe.Visualization.Configurations;

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Provides extension methods for configuring PowerPipe.Visualization in Microsoft Dependency Injection (DI) services.
/// </summary>
public static class ServiceCollectionExtension
{
/// <summary>
/// Registers Services required for pipeline visualization
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="configuration">The action used to configure the options</param>
/// <returns>Service collection</returns>
public static IServiceCollection AddPowerPipeVisualization(
this IServiceCollection services, Action<PowerPipeVisualizationConfiguration> configuration)
{
var config = new PowerPipeVisualizationConfiguration();
configuration.Invoke(config);

services.AddSingleton(_ => Options.Options.Create(config));

return services.AddSingleton<IPipelineDiagramService, PipelineDiagramsService>();
}
}
26 changes: 26 additions & 0 deletions src/PowerPipe.Visualization/Antlr/Extensions/ParserExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using Antlr4.Runtime.Tree;

namespace PowerPipe.Visualization.Antlr.Extensions;

internal static class ParserExtensions
{
internal static string GetStepName(this ITerminalNode node) =>
node.GetText().TrimStart('<').TrimEnd('>').Split('<')[0];

internal static (string, string) GetTwoStepsNames(this ITerminalNode node)
{
var steps = node.GetText().Split(',', StringSplitOptions.RemoveEmptyEntries);

var step1 = steps[0].TrimStart('<').TrimEnd('>').Split('<')[0];
var step2 = steps[1].TrimStart('<').TrimEnd('>').Split('<')[0];

return (step1, step2);
}

internal static string GetPredicateName(this ITerminalNode node) =>
node.GetText().TrimStart('(').TrimEnd(')');

internal static string GetOpenPredicateName(this ITerminalNode node) =>
node.GetText().TrimStart('(').TrimEnd(',');
}
44 changes: 44 additions & 0 deletions src/PowerPipe.Visualization/Antlr/PipelineLexer.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
lexer grammar PipelineLexer;

// Temporary skipping these
ONERRORRETRY: '.OnError(PipelineStepErrorHandling.Retry)' -> skip;
ONERRORSUPPRESS: '.OnError(PipelineStepErrorHandling.Suppress)' -> skip;
COMPENSATE: '.CompensateWith' DATA '()' -> skip;

LEFTARROW: '<' -> skip;
RIGHTARROW: '>' -> skip;
EMPTYPAR: '()' -> skip;
COMA: ',' -> skip;
DOT: '.' -> skip;
WS: [ \t\r\n]+ -> skip;

NEWBUIDLER: 'new PipelineBuilder<' (([a-zA-Z0-9_]+'<'[a-zA-Z0-9_]+'>' | [a-zA-Z0-9_]*)) ', '
(([a-zA-Z0-9_]+'<'[a-zA-Z0-9_]+'>' | [a-zA-Z0-9_]*)) '>' '('
(([a-zA-Z0-9_]+'<'[a-zA-Z0-9_]+'>' | [a-zA-Z0-9_]*)) ', '
(([a-zA-Z0-9_]+'<'[a-zA-Z0-9_]+'>' | [a-zA-Z0-9_]*)) ')' -> skip;

LAMBDANAME: [a-z]+ -> skip;
LAMBDA: '(PipelineBuilder<' ([a-zA-Z0-9_]+'<'[a-zA-Z0-9_]+'>' | [a-zA-Z0-9_]*) ', '
([a-zA-Z0-9_]+'<'[a-zA-Z0-9_]+'>' | [a-zA-Z0-9_]*) '> '
([a-zA-Z0-9_]+'<'[a-zA-Z0-9_]+'>' | [a-zA-Z0-9_]*) ') =>' -> skip;

ADD: 'Add';
ADDIF: 'AddIf';
ADDIFELSE: 'AddIfElse';
PARALLEL: 'Parallel';
IF: 'If';
PREDICATE: '(' [a-zA-Z][a-zA-Z0-9_]* ')';
OPENPREDICATE: '(' [a-zA-Z][a-zA-Z0-9_]* ',';
DATA: (STEPWITHGENERIC | STEPWITHOUTGENERIC);
DATA2: (TWOSTEPSWITHGENERIC | TWOSTEPSWITHOUTGENERIC);

STEPWITHGENERIC: '<' [A-Za-z_0-9]+ '<' [A-Za-z_0-9]+ '>' '>';
STEPWITHOUTGENERIC: '<' [A-Za-z_0-9]+ '>';

TWOSTEPSWITHGENERIC: '<' [A-Za-z_0-9]+ '<' [A-Za-z_0-9]+ '>' ', ' [A-Za-z_0-9]+ '<' [A-Za-z_0-9]+ '>' '>';
TWOSTEPSWITHOUTGENERIC: '<' [A-Za-z_0-9]+ ', ' [A-Za-z_0-9]+ '>';

ANYTEXT: ('A'..'Z' | 'a'..'z' | '0'..'9' | '_')+;

OPENPAR: '(';
CLOSEPAR: ')';
42 changes: 42 additions & 0 deletions src/PowerPipe.Visualization/Antlr/PipelineLexer.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
ONERRORRETRY=1
ONERRORSUPPRESS=2
COMPENSATE=3
LEFTARROW=4
RIGHTARROW=5
EMPTYPAR=6
COMA=7
DOT=8
WS=9
NEWBUIDLER=10
LAMBDANAME=11
LAMBDA=12
ADD=13
ADDIF=14
ADDIFELSE=15
PARALLEL=16
IF=17
PREDICATE=18
OPENPREDICATE=19
DATA=20
DATA2=21
STEPWITHGENERIC=22
STEPWITHOUTGENERIC=23
TWOSTEPSWITHGENERIC=24
TWOSTEPSWITHOUTGENERIC=25
ANYTEXT=26
OPENPAR=27
CLOSEPAR=28
'.OnError(PipelineStepErrorHandling.Retry)'=1
'.OnError(PipelineStepErrorHandling.Suppress)'=2
'<'=4
'>'=5
'()'=6
','=7
'.'=8
'Add'=13
'AddIf'=14
'AddIfElse'=15
'Parallel'=16
'If'=17
'('=27
')'=28
13 changes: 13 additions & 0 deletions src/PowerPipe.Visualization/Antlr/PipelineParser.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
parser grammar PipelineParser;

options { tokenVocab = PipelineLexer; }

start: step+;

step: addStep | addIfStep | addIfElseStep | ifStep | parallelStep;

addStep: ADD DATA;
addIfStep: ADDIF DATA PREDICATE;
addIfElseStep: ADDIFELSE DATA2 PREDICATE;
ifStep: IF OPENPREDICATE step+ CLOSEPAR;
parallelStep: PARALLEL OPENPAR step+ CLOSEPAR;
Loading
Loading