Skip to content

Commit 4946620

Browse files
initial check in
1 parent 11dec28 commit 4946620

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2851
-12
lines changed

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ on:
66

77
env:
88
BRANCH_NAME: ${{ github.event.release.target_commitish }}
9-
SOLUTION_NAME: ${{ vars.SOLUTION_NAME }}
10-
PROJECT_NAME: ${{ vars.PROJECT_NAME }}
9+
SOLUTION_NAME: 'Hyperbee.Pipeline.sln'
10+
PROJECT_NAME: 'Hyperbee.Pipeline'
1111
DOTNET_VERSION: '8.0.x'
1212
NUGET_SOURCE: 'https://api.nuget.org/v3/index.json'
1313
BUILD_CONFIGURATION: ''

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ on:
1616

1717
env:
1818
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
19-
SOLUTION_NAME: ${{ vars.SOLUTION_NAME }}
19+
SOLUTION_NAME: 'Hyperbee.Pipeline.sln'
2020
DOTNET_VERSION: '8.0.x'
2121

2222
jobs:

.github/workflows/unlist-nuget.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
env:
99
BRANCH_NAME: ${{ github.event.release.target_commitish }}
10-
PROJECT_NAME: ${{ vars.PROJECT_NAME }}
10+
PROJECT_NAME: 'Hyperbee.Pipeline'
1111

1212
jobs:
1313
publish:

Hyperbee.Pipeline.sln

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31912.275
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{870D9301-BE3D-44EA-BF9C-FCC2E87FE4CD}"
7+
ProjectSection(SolutionItems) = preProject
8+
Directory.Build.props = Directory.Build.props
9+
Directory.Build.targets = Directory.Build.targets
10+
LICENSE = LICENSE
11+
README.md = README.md
12+
EndProjectSection
13+
EndProject
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Pipeline", "src\Hyperbee.Pipeline\Hyperbee.Pipeline.csproj", "{6725FAD8-FADC-413A-B352-0CF583DD1CC2}"
15+
EndProject
16+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Tests", "Solution Tests", "{F9B24CD9-E06B-4834-84CB-8C29E5F10BE0}"
17+
EndProject
18+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1FA7CE2A-C9DA-4DC3-A242-5A7EAF8EE4FC}"
19+
EndProject
20+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{4DBDB7F5-3F66-4572-80B5-3322449C77A4}"
21+
ProjectSection(SolutionItems) = preProject
22+
.github\workflows\publish.yml = .github\workflows\publish.yml
23+
.github\workflows\test.yml = .github\workflows\test.yml
24+
.github\workflows\update-version.yml = .github\workflows\update-version.yml
25+
EndProjectSection
26+
EndProject
27+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Pipeline.Tests", "test\Hyperbee.Pipeline.Tests\Hyperbee.Pipeline.Tests.csproj", "{17DA1657-DF82-440F-B1F1-D888BFA9626B}"
28+
EndProject
29+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{884A8242-351E-4363-9B34-E8C202CF7787}"
30+
ProjectSection(SolutionItems) = preProject
31+
docs\middleware.md = docs\middleware.md
32+
EndProjectSection
33+
EndProject
34+
Global
35+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
36+
Debug|Any CPU = Debug|Any CPU
37+
Release|Any CPU = Release|Any CPU
38+
EndGlobalSection
39+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
40+
{6725FAD8-FADC-413A-B352-0CF583DD1CC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41+
{6725FAD8-FADC-413A-B352-0CF583DD1CC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
42+
{6725FAD8-FADC-413A-B352-0CF583DD1CC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
43+
{6725FAD8-FADC-413A-B352-0CF583DD1CC2}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{17DA1657-DF82-440F-B1F1-D888BFA9626B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{17DA1657-DF82-440F-B1F1-D888BFA9626B}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{17DA1657-DF82-440F-B1F1-D888BFA9626B}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{17DA1657-DF82-440F-B1F1-D888BFA9626B}.Release|Any CPU.Build.0 = Release|Any CPU
48+
EndGlobalSection
49+
GlobalSection(SolutionProperties) = preSolution
50+
HideSolutionNode = FALSE
51+
EndGlobalSection
52+
GlobalSection(NestedProjects) = preSolution
53+
{1FA7CE2A-C9DA-4DC3-A242-5A7EAF8EE4FC} = {870D9301-BE3D-44EA-BF9C-FCC2E87FE4CD}
54+
{4DBDB7F5-3F66-4572-80B5-3322449C77A4} = {1FA7CE2A-C9DA-4DC3-A242-5A7EAF8EE4FC}
55+
{17DA1657-DF82-440F-B1F1-D888BFA9626B} = {F9B24CD9-E06B-4834-84CB-8C29E5F10BE0}
56+
{884A8242-351E-4363-9B34-E8C202CF7787} = {870D9301-BE3D-44EA-BF9C-FCC2E87FE4CD}
57+
EndGlobalSection
58+
GlobalSection(ExtensibilityGlobals) = postSolution
59+
SolutionGuid = {32874F5B-B467-4F28-A8E2-82C2536FB228}
60+
EndGlobalSection
61+
EndGlobal

Hyperbee.Pipeline.sln.DotSettings

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Hyperbee/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

README.md

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,43 @@
1-
# Hyperbee.Project
1+
# Hyperbee.Pipeline
22

3-
Classes for building awesome software
3+
Classes for building composable async pipelines supporting:
4+
5+
* Value projections
6+
* Early returns
7+
* Child pipelines
8+
* [Middleware](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/blob/main/docs/middleware.md)
9+
* [Conditional flow](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/blob/main/docs/execution.md)
10+
* [Dependency Injection](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/blob/main/docs/dependencyInjection.md)
411

512
## Usage
613

714
```csharp
8-
# Cool usage of the code!
15+
// Takes a string and returns a number
16+
var question = PipelineFactory
17+
.Start<string>()
18+
.PipeIf((ctx, arg) => arg == "Adams", builder => builder
19+
.Pipe((ctx, arg) => 42)
20+
.Cancel()
21+
)
22+
.Pipe((ctx, arg) => 0)
23+
.Build();
24+
25+
var answer1 = await question(new PipelineContext(), "Adams");
26+
Assert.AreEqual(42, answer1);
27+
28+
var answer2 = await question(new PipelineContext(), "Smith");
29+
Assert.AreEqual(0, answer2);
930
```
1031

1132
# Status
1233

1334
| Branch | Action |
1435
|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
15-
| `develop` | [![Build status](https://github.com/Stillpoint-Software/Hyperbee.Project/actions/workflows/publish.yml/badge.svg?branch=develop)](https://github.com/Stillpoint-Software/Hyperbee.Project/actions/workflows/publish.yml) |
16-
| `main` | [![Build status](https://github.com/Stillpoint-Software/Hyperbee.Project/actions/workflows/publish.yml/badge.svg)](https://github.com/Stillpoint-Software/Hyperbee.Project/actions/workflows/publish.yml) |
36+
| `develop` | [![Build status](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/actions/workflows/publish.yml/badge.svg?branch=develop)](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/actions/workflows/publish.yml) |
37+
| `main` | [![Build status](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/actions/workflows/publish.yml/badge.svg)](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/actions/workflows/publish.yml) |
1738

1839

19-
[![Hyperbee.Project](https://github.com/Stillpoint-Software/Hyperbee.Project/blob/main/assets/hyperbee.jpg?raw=true)](https://github.com/Stillpoint-Software/Hyperbee.Project)
40+
[![Hyperbee.Pipeline](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/blob/main/assets/hyperbee.jpg?raw=true)](https://github.com/Stillpoint-Software/Hyperbee.Pipeline)
2041

2142
# Help
22-
See [Todo](https://github.com/Stillpoint-Software/Hyperbee.Project/blob/main/docs/todo.md)
43+
See [Todo](https://github.com/Stillpoint-Software/Hyperbee.Pipeline/blob/main/docs/todo.md)

docs/childPipeline.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Child Pipleline

docs/dependencyInjection.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Dependency Injection
2+
3+
## Dependency Injection
4+
5+
To use pipelines with Dependency Injection call `AddPipeline()`, or one of its overloads, when configuring
6+
the application.
7+
8+
### Example 1
9+
Register pipelines with DI using defaults.
10+
11+
```csharp
12+
services.AddPipeline();
13+
```
14+
15+
## Pipeline dependencies
16+
17+
Sometimes Pipelines and Pipeline middleware need access to specific container services. This can be
18+
accomplished by registering services with the `PipelineContextFactory`. This can be done through
19+
DI configuration, or manually through the `PipelineContextFactoryProvider` if you are not using DI.
20+
21+
Pipelines manage dependencies with a specialized container. This allows the implementor to control
22+
the services that are exposed through the pipeline. If you want to expose all application
23+
services then you can call `AddPipeline` and pass `includeAllServices: true`.
24+
25+
### Example 2
26+
Register pipelines with DI and provide Pipeline dependencies using the application container.
27+
28+
```csharp
29+
services.AddPipeline( includeAllServices: true );
30+
```
31+
32+
### Example 3
33+
Register Pipelines with DI and provide Pipeline dependencies using a specialized container.
34+
35+
```csharp
36+
services.AddPipeline( (factoryServices, rootProvider) =>
37+
{
38+
factoryServices.AddTransient<IThing>()
39+
factoryServices.ProxyService<IPrincipalProvider>( rootProvider ); // pull from root container
40+
} );
41+
```
42+
43+
### Example 4
44+
Register Pipeline services manually and provide Pipeline dependencies using a specialized container.
45+
46+
```csharp
47+
var serviceProvider = new ServiceCollection()
48+
.AddTransient<IPrincipalProvider>()
49+
.BuildServiceProvider();
50+
51+
PipelineContextFactory.CreateFactory( serviceProvider );
52+
53+
// get the `PipelineContextFactory` without DI
54+
var factory = PipelineContextFactory.Instance;
55+
```
56+
57+
Once you have registered pipeline services, custom middleware can get service instances at runtime
58+
by accessing the `context.ServiceProvider` property.
59+
60+
```csharp
61+
context.ServiceProvider.GetRequiredService<IPrincipleProvider>()
62+
```

docs/execution.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
2+
3+
# Execution
4+
5+
| Method | Description
6+
| ---------- | -----------
7+
| Call | Execute a `void` step that does not transform the pipeline output.
8+
| CallAsync | Asynchronously execute a `void` step that does not transform the pipeline output.
9+
| Pipe | Execute a step that transforms the pipeline output.
10+
| PipeAsync | Asynchronously execute a step that transforms the pipeline output.
11+
12+
## Flow Control
13+
14+
| Method | Description
15+
| ---------- | -----------
16+
| Cancel | Cancels the pipeline after the current step.
17+
| CancelWith | Cancels the pipeline with a value, after the current step.
18+
| Pipe | Pipes a child pipeline with optional middlewares.
19+
| PipeIf | Conditionally pipes a child pipeline with optional middlewares.
20+
| Call | Calls a child pipeline with optional middlewares.
21+
| CallIf | Conditionally calls a child pipeline with optional middlewares.
22+
| ForEach | Enumerates a collection pipeline input.
23+
| Reduce | Transforms an enumerable pipeline input.
24+
25+
## Parallel Flow
26+
27+
| Method | Description
28+
| ---------- | -----------
29+
| WaitAll | Waits for concurrent pipelines to complete.
30+
31+
## Middleware
32+
33+
| Method | Description
34+
| ---------- | -----------
35+
| Hook | Applies middleware to each step in the pipeline.
36+
| Wrap | Wraps the middleware around the preceeding steps.
37+
38+
## Building and Executing Pipelines
39+
40+
Pipelines are built using `PipelineFactory`. Once built, a pipeline is just an async function that takes a `PipelineContext` and
41+
an optional input value as parameters, and returns a result.
42+
43+
```csharp
44+
var command = PipelineFactory
45+
.Start<string>()
46+
.Pipe( ( ctx, arg ) => $"hello {arg}" )
47+
.Build();
48+
49+
var result = await command( new PipelineContext(), "pipeline" );
50+
51+
Assert.AreEqual( "hello pipeline", result );
52+
```
53+
54+
## Command Pattern
55+
56+
`ICommand*` interfaces and `Command*` base classes provide a lightweight pattern for constructing injectable commands built
57+
around pipelines and middleware.
58+
59+
| Interface | Class | Description
60+
| -------------------------------------- | ------------------------------------- | ---------------------------------------------------
61+
| ICommandFunction&lt;TInput,TOutput&gt; | CommandFunction&lt;TInput,TOutput&gt; | A command that takes an input and returns an output
62+
| ICommandFunction&lt;TOutput&gt; | CommandFunction&lt;TOutput&gt; | A command that takes no input and returns an output
63+
| ICommandProcedure&lt;TInput&gt; | CommandProcedure&lt;TInput&gt; | A command that takes an input and returns void
64+
65+
#### Example 1
66+
Example of a command that takes an input and produces an output.
67+
68+
```csharp
69+
public interface IMyCommand : ICommandFunction<Guid, String>
70+
{
71+
}
72+
73+
public class MyCommand : CommandFunction<Guid, String>, IMyCommand
74+
{
75+
public MyCommand( ILogger<GetMessageCommand> logger )
76+
: base( logger)
77+
{
78+
}
79+
80+
protected override FunctionAsync<Guid, String> PipelineFactory()
81+
{
82+
return PipelineFactory
83+
.Start<Guid>()
84+
.WithLogging()
85+
.Pipe( GetString )
86+
.Build();
87+
}
88+
89+
private async Task<String> GetString( IPipelineContext context, Guid id )
90+
{
91+
return id.ToString();
92+
}
93+
}
94+
95+
// usage
96+
void usage( IMyCommand command )
97+
{
98+
var result = await command.ExecuteAsync( Guid.Create() ); // takes a Guid, returns a string
99+
}
100+
```
101+
102+
#### Example 2
103+
Example of a command that takes no input and produces an output.
104+
105+
```csharp
106+
public interface IMyCommand : ICommandFunction<String>
107+
{
108+
}
109+
110+
public class MyCommand : CommandFunction<String>, IMyCommand
111+
{
112+
public MyCommand( ILogger<GetMessageCommand> logger )
113+
: base( logger)
114+
{
115+
}
116+
117+
protected override FunctionAsync<Arg.Empty, String> CreatePipeline()
118+
{
119+
return PipelineFactory
120+
.Start<Arg.Empty>()
121+
.WithLogging()
122+
.PipeAsync( GetString )
123+
.Build();
124+
}
125+
126+
private String GetString( IPipelineContext context, Arg.Empty _ )
127+
{
128+
return "Hello";
129+
}
130+
}
131+
132+
// usage
133+
void usage( IMyCommand command )
134+
{
135+
var result = await command.ExecuteAsync(); // returns "Hello"
136+
}
137+
```
138+
139+
#### Example 3
140+
Example of a command that takes an input and produces no output.
141+
142+
```csharp
143+
public interface IMyCommand : ICommandProcedure<String>
144+
{
145+
}
146+
147+
public class MyCommand : CommandProcedure<String>, IMyCommand
148+
{
149+
public GetCommand( ILogger<MyCommand> logger )
150+
: base( logger)
151+
{
152+
}
153+
154+
protected override ProcedureAsync<String> CreatePipeline()
155+
{
156+
return PipelineFactory
157+
.Start<String>()
158+
.WithLogging()
159+
.PipeAsync( ExecSomeAction )
160+
.BuildAsProcedure();
161+
}
162+
163+
private String ExecSomeAction( IPipelineContext context, String who )
164+
{
165+
return $"Hello {who}";
166+
}
167+
}
168+
169+
// usage
170+
void usage( IMyCommand command )
171+
{
172+
var result = await command.ExecuteAsync( "me" ); // returns "Hello me"
173+
}
174+
```

0 commit comments

Comments
 (0)