Add StellaOps.Workflow engine: 14 libraries, WebService, 8 test projects

Extract product-agnostic workflow engine from Ablera.Serdica.Workflow into
standalone StellaOps.Workflow.* libraries targeting net10.0.

Libraries (14):
- Contracts, Abstractions (compiler, decompiler, expression runtime)
- Engine (execution, signaling, scheduling, projections, hosted services)
- ElkSharp (generic graph layout algorithm)
- Renderer.ElkSharp, Renderer.ElkJs, Renderer.Msagl, Renderer.Svg
- Signaling.Redis, Signaling.OracleAq
- DataStore.MongoDB, DataStore.PostgreSQL, DataStore.Oracle

WebService: ASP.NET Core Minimal API with 22 endpoints

Tests (8 projects, 109 tests pass):
- Engine.Tests (105 pass), WebService.Tests (4 E2E pass)
- Renderer.Tests, DataStore.MongoDB/Oracle/PostgreSQL.Tests
- Signaling.Redis.Tests, IntegrationTests.Shared

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-20 19:14:44 +02:00
parent e56f9a114a
commit f5b5f24d95
422 changed files with 85428 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
using WorkflowEngine.Abstractions;
namespace WorkflowEngine.Tutorials;
/// <summary>
/// Expression builder examples showing all expression types.
/// These examples are not a runnable workflow — they demonstrate the WorkflowExpr API.
/// </summary>
internal static class ExpressionExamples
{
// ═══════════════════════════════════════════════
// LITERALS
// ═══════════════════════════════════════════════
static readonly WorkflowExpressionDefinition nullExpr = WorkflowExpr.Null();
static readonly WorkflowExpressionDefinition stringExpr = WorkflowExpr.String("hello");
static readonly WorkflowExpressionDefinition numberExpr = WorkflowExpr.Number(42);
static readonly WorkflowExpressionDefinition boolExpr = WorkflowExpr.Bool(true);
// ═══════════════════════════════════════════════
// PATH NAVIGATION
// ═══════════════════════════════════════════════
// Read from start request
static readonly WorkflowExpressionDefinition fromStart = WorkflowExpr.Path("start.policyId");
// Read from workflow state
static readonly WorkflowExpressionDefinition fromState = WorkflowExpr.Path("state.customerName");
// Read from task completion payload
static readonly WorkflowExpressionDefinition fromPayload = WorkflowExpr.Path("payload.answer");
// Read from step result (requires resultKey on the Call step)
static readonly WorkflowExpressionDefinition fromResult = WorkflowExpr.Path("result.productInfo.lob");
// Nested path navigation
static readonly WorkflowExpressionDefinition nested = WorkflowExpr.Path("state.entityData.address.city");
// ═══════════════════════════════════════════════
// OBJECT & ARRAY CONSTRUCTION
// ═══════════════════════════════════════════════
static readonly WorkflowExpressionDefinition obj = WorkflowExpr.Object(
WorkflowExpr.Prop("policyId", WorkflowExpr.Path("state.policyId")),
WorkflowExpr.Prop("status", WorkflowExpr.String("ACTIVE")),
WorkflowExpr.Prop("tags", WorkflowExpr.Array(
WorkflowExpr.String("motor"),
WorkflowExpr.String("casco"))));
// ═══════════════════════════════════════════════
// COMPARISONS
// ═══════════════════════════════════════════════
static readonly WorkflowExpressionDefinition eq = WorkflowExpr.Eq(
WorkflowExpr.Path("state.status"), WorkflowExpr.String("APPROVED"));
static readonly WorkflowExpressionDefinition ne = WorkflowExpr.Ne(
WorkflowExpr.Path("state.status"), WorkflowExpr.String("REJECTED"));
static readonly WorkflowExpressionDefinition gt = WorkflowExpr.Gt(
WorkflowExpr.Path("state.premium"), WorkflowExpr.Number(1000));
// ═══════════════════════════════════════════════
// BOOLEAN LOGIC
// ═══════════════════════════════════════════════
static readonly WorkflowExpressionDefinition not = WorkflowExpr.Not(
WorkflowExpr.Path("state.isRejected"));
static readonly WorkflowExpressionDefinition and = WorkflowExpr.And(
WorkflowExpr.Path("state.isValid"),
WorkflowExpr.Not(WorkflowExpr.Path("state.isRejected")));
static readonly WorkflowExpressionDefinition or = WorkflowExpr.Or(
WorkflowExpr.Eq(WorkflowExpr.Path("state.status"), WorkflowExpr.String("APPROVED")),
WorkflowExpr.Eq(WorkflowExpr.Path("state.status"), WorkflowExpr.String("OVERRIDE")));
// ═══════════════════════════════════════════════
// FUNCTION CALLS
// ═══════════════════════════════════════════════
// Coalesce: first non-null value
static readonly WorkflowExpressionDefinition coalesce = WorkflowExpr.Func("coalesce",
WorkflowExpr.Path("state.customerId"),
WorkflowExpr.Path("start.customerId"),
WorkflowExpr.Number(0));
// String concatenation
static readonly WorkflowExpressionDefinition concat = WorkflowExpr.Func("concat",
WorkflowExpr.String("Policy #"),
WorkflowExpr.Path("state.policyNo"));
// Arithmetic
static readonly WorkflowExpressionDefinition increment = WorkflowExpr.Func("add",
WorkflowExpr.Func("coalesce",
WorkflowExpr.Path("state.attempt"), WorkflowExpr.Number(0)),
WorkflowExpr.Number(1));
// Conditional value
static readonly WorkflowExpressionDefinition conditional = WorkflowExpr.Func("if",
WorkflowExpr.Path("state.isVip"),
WorkflowExpr.String("VIP"),
WorkflowExpr.String("Standard"));
// Uppercase
static readonly WorkflowExpressionDefinition upper = WorkflowExpr.Func("upper",
WorkflowExpr.Path("state.annexType"));
// Null check
static readonly WorkflowExpressionDefinition nullCheck = WorkflowExpr.Func("isNullOrWhiteSpace",
WorkflowExpr.Path("state.integrationId"));
// Array length
static readonly WorkflowExpressionDefinition length = WorkflowExpr.Func("length",
WorkflowExpr.Path("state.documents"));
// ═══════════════════════════════════════════════
// COMBINING EXPRESSIONS (real-world patterns)
// ═══════════════════════════════════════════════
// "Use integration customer ID if present, otherwise use lookup ID"
static readonly WorkflowExpressionDefinition resolveCustomerId = WorkflowExpr.Func("if",
WorkflowExpr.Not(
WorkflowExpr.Func("isNullOrWhiteSpace",
WorkflowExpr.Path("state.integrationCustomerId"))),
WorkflowExpr.Path("state.integrationCustomerId"),
WorkflowExpr.Path("state.lookupCustomerId"));
// "Should we retry? (first attempt or previous failed, and not timed out)"
static readonly WorkflowExpressionDefinition shouldRetry = WorkflowExpr.Or(
WorkflowExpr.Eq(WorkflowExpr.Path("state.retryAttempt"), WorkflowExpr.Number(0)),
WorkflowExpr.And(
WorkflowExpr.Not(WorkflowExpr.Path("state.timedOut")),
WorkflowExpr.Path("state.integrationFailed")));
}