Skip to content
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 3.2.1 - 2025.04.24

### Modified

- Code readability improvements in `WorkflowBuilder`

## 3.2.0 - 2025-04-24

### Added
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<Description>A .NET library for building robust, functional workflows and processing pipelines.</Description>

<!-- Version information -->
<Version>3.2.0</Version>
<Version>3.2.1</Version>

<!-- Source linking -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
Expand Down
130 changes: 83 additions & 47 deletions Zooper.Bee.Example/ParameterlessWorkflowExample.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Zooper.Fox;
using Zooper.Bee.Extensions;

// ReSharper disable ClassNeverInstantiated.Global

namespace Zooper.Bee.Example;

public class ParameterlessWorkflowExample
public sealed class ParameterlessWorkflowExample
{
// Success model
public record ProcessingResult(DateTime ProcessedAt, string Status);
Expand Down Expand Up @@ -43,17 +46,28 @@ private static async Task RunExampleWithFactory()
// Configure the workflow
builder => builder
.Do(payload =>
{
Console.WriteLine("Processing step 1...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with { Status = "Step 1 completed" });
})
{
Console.WriteLine("Processing step 1...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with
{
Status = "Step 1 completed"
}
);
}
)
.Do(payload =>
{
Console.WriteLine("Processing step 2...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with { Status = "Step 2 completed", IsCompleted = true });
})
{
Console.WriteLine("Processing step 2...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with
{
Status = "Step 2 completed",
IsCompleted = true
}
);
}
)
);

// Execute without parameters
Expand All @@ -74,25 +88,36 @@ private static async Task RunExampleWithUnit()
{
// Create a workflow with Unit type as request
var workflow = new WorkflowBuilder<Unit, ProcessingPayload, ProcessingResult, ProcessingError>(
// Use Unit parameter (ignored)
_ => new ProcessingPayload(StartedAt: DateTime.UtcNow),
// Use Unit parameter (ignored)
_ => new ProcessingPayload(StartedAt: DateTime.UtcNow),

// Result selector
payload => new ProcessingResult(DateTime.UtcNow, payload.Status)
)
.Do(payload =>
{
Console.WriteLine("Executing task A...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with { Status = "Task A completed" });
})
.Do(payload =>
{
Console.WriteLine("Executing task B...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with { Status = "Task B completed", IsCompleted = true });
})
.Build();
// Result selector
payload => new ProcessingResult(DateTime.UtcNow, payload.Status)
)
.Do(payload =>
{
Console.WriteLine("Executing task A...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with
{
Status = "Task A completed"
}
);
}
)
.Do(payload =>
{
Console.WriteLine("Executing task B...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with
{
Status = "Task B completed",
IsCompleted = true
}
);
}
)
.Build();

// Execute with Unit.Value
var result = await workflow.Execute(Unit.Value);
Expand All @@ -112,25 +137,36 @@ private static async Task RunExampleWithExtension()
{
// Create a workflow with Unit type as request
var workflow = new WorkflowBuilder<Unit, ProcessingPayload, ProcessingResult, ProcessingError>(
// Use Unit parameter (ignored)
_ => new ProcessingPayload(StartedAt: DateTime.UtcNow),
// Use Unit parameter (ignored)
_ => new ProcessingPayload(StartedAt: DateTime.UtcNow),

// Result selector
payload => new ProcessingResult(DateTime.UtcNow, payload.Status)
)
.Do(payload =>
{
Console.WriteLine("Running process X...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with { Status = "Process X completed" });
})
.Do(payload =>
{
Console.WriteLine("Running process Y...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with { Status = "Process Y completed", IsCompleted = true });
})
.Build();
// Result selector
payload => new ProcessingResult(DateTime.UtcNow, payload.Status)
)
.Do(payload =>
{
Console.WriteLine("Running process X...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with
{
Status = "Process X completed"
}
);
}
)
.Do(payload =>
{
Console.WriteLine("Running process Y...");
return Either<ProcessingError, ProcessingPayload>.FromRight(
payload with
{
Status = "Process Y completed",
IsCompleted = true
}
);
}
)
.Build();

// Execute using the extension method (no parameters)
var result = await workflow.Execute();
Expand Down
106 changes: 73 additions & 33 deletions Zooper.Bee.Tests/ParameterlessWorkflowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
using FluentAssertions;
using Xunit;
using Zooper.Fox;
using Zooper.Bee.Extensions;

namespace Zooper.Bee.Tests;

public class ParameterlessWorkflowTests
{
#region Test Models

// Payload model for tests
private record TestPayload(DateTime StartTime, string Status = "Waiting");

Expand All @@ -17,24 +19,35 @@ private record TestSuccess(string Status, bool IsComplete);

// Error model
private record TestError(string Code, string Message);

#endregion

[Fact]
public async Task ParameterlessWorkflow_UsingUnitType_CanBeExecuted()
{
// Arrange
var workflow = new WorkflowBuilder<Unit, TestPayload, TestSuccess, TestError>(
// Convert Unit to initial payload
_ => new TestPayload(DateTime.UtcNow),

// Convert final payload to success result
payload => new TestSuccess(payload.Status, true)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with { Status = "Processing" }))
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with { Status = "Completed" }))
.Build();
// Convert Unit to initial payload
_ => new TestPayload(DateTime.UtcNow),

// Convert final payload to success result
payload => new TestSuccess(payload.Status, true)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with
{
Status = "Processing"
}
)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with
{
Status = "Completed"
}
)
)
.Build();

// Act
var result = await workflow.Execute(Unit.Value);
Expand All @@ -59,9 +72,19 @@ public async Task ParameterlessWorkflow_UsingFactory_CanBeExecuted()
// Configure the workflow
builder => builder
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with { Status = "Processing" }))
payload with
{
Status = "Processing"
}
)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with { Status = "Completed" }))
payload with
{
Status = "Completed"
}
)
)
);

// Act
Expand All @@ -78,14 +101,24 @@ public async Task ParameterlessWorkflow_UsingExtensionMethod_CanBeExecuted()
{
// Arrange
var workflow = new WorkflowBuilder<Unit, TestPayload, TestSuccess, TestError>(
_ => new TestPayload(DateTime.UtcNow),
payload => new TestSuccess(payload.Status, true)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with { Status = "Processing" }))
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with { Status = "Completed" }))
.Build();
_ => new TestPayload(DateTime.UtcNow),
payload => new TestSuccess(payload.Status, true)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with
{
Status = "Processing"
}
)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with
{
Status = "Completed"
}
)
)
.Build();

// Act - using extension method (no parameters)
var result = await workflow.Execute();
Expand All @@ -101,18 +134,25 @@ public async Task ParameterlessWorkflow_WithError_ReturnsError()
{
// Arrange
var workflow = WorkflowBuilderFactory.Create<TestPayload, TestSuccess, TestError>(
() => new TestPayload(DateTime.UtcNow),
payload => new TestSuccess(payload.Status, true)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with { Status = "Processing" }))
.Do(payload =>
{
// Simulate an error in the workflow
return Either<TestError, TestPayload>.FromLeft(
new TestError("PROCESSING_FAILED", "Failed to complete processing"));
})
.Build();
() => new TestPayload(DateTime.UtcNow),
payload => new TestSuccess(payload.Status, true)
)
.Do(payload => Either<TestError, TestPayload>.FromRight(
payload with
{
Status = "Processing"
}
)
)
.Do(payload =>
{
// Simulate an error in the workflow
return Either<TestError, TestPayload>.FromLeft(
new TestError("PROCESSING_FAILED", "Failed to complete processing")
);
}
)
.Build();

// Act
var result = await workflow.Execute();
Expand Down
Loading