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:
@@ -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")));
|
||||
}
|
||||
Reference in New Issue
Block a user