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,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using WorkflowEngine.Abstractions;
|
||||
using WorkflowEngine.Contracts;
|
||||
|
||||
namespace WorkflowEngine.Tutorials;
|
||||
|
||||
/// <summary>
|
||||
/// Shared support helper for policy change workflows.
|
||||
/// Centralizes addresses, payload builders, and reusable flow patterns.
|
||||
/// </summary>
|
||||
internal static class PolicyWorkflowSupport
|
||||
{
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// ADDRESS REGISTRY
|
||||
// Centralize all service routing in one place.
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
internal static readonly LegacyRabbitAddress ValidatePolicyAddress =
|
||||
new("pas_policy_validate");
|
||||
|
||||
internal static readonly LegacyRabbitAddress AlterPolicyAddress =
|
||||
new("pas_annexprocessing_alterpolicy");
|
||||
|
||||
internal static readonly LegacyRabbitAddress CalculatePremiumAddress =
|
||||
new("pas_premium_calculate_for_object",
|
||||
SerdicaLegacyRabbitMode.MicroserviceConsumer);
|
||||
|
||||
internal static readonly LegacyRabbitAddress GetAnnexDescAddress =
|
||||
new("pas_polannexes_get");
|
||||
|
||||
internal static readonly LegacyRabbitAddress NotificationEmailAddress =
|
||||
new("notifications_send_email",
|
||||
SerdicaLegacyRabbitMode.MicroserviceConsumer);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// WORKFLOW REFERENCES
|
||||
// For SubWorkflow and ContinueWith invocations.
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
internal static readonly WorkflowReference ReviewPolicyReference =
|
||||
new("ReviewPolicyOpenForChange");
|
||||
|
||||
internal static readonly WorkflowReference TransferPolicyReference =
|
||||
new("TransferPolicy");
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// STATE INITIALIZATION
|
||||
// Base state + override pattern for workflow families.
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>
|
||||
/// Builds a state initialization expression with common policy fields
|
||||
/// and optional per-workflow overrides.
|
||||
/// </summary>
|
||||
internal static WorkflowExpressionDefinition BuildInitializeState(
|
||||
params WorkflowNamedExpressionDefinition[] overrides)
|
||||
{
|
||||
var properties = new List<WorkflowNamedExpressionDefinition>
|
||||
{
|
||||
WorkflowExpr.Prop("srPolicyId", WorkflowExpr.Path("start.srPolicyId")),
|
||||
WorkflowExpr.Prop("srAnnexId", WorkflowExpr.Path("start.srAnnexId")),
|
||||
WorkflowExpr.Prop("srCustId", WorkflowExpr.Path("start.srCustId")),
|
||||
WorkflowExpr.Prop("annexType", WorkflowExpr.Path("start.annexType")),
|
||||
WorkflowExpr.Prop("beginDate", WorkflowExpr.Path("start.beginDate")),
|
||||
WorkflowExpr.Prop("endDate", WorkflowExpr.Path("start.endDate")),
|
||||
};
|
||||
|
||||
// Apply overrides: replace existing or add new properties.
|
||||
foreach (var o in overrides)
|
||||
{
|
||||
var existing = properties.FindIndex(
|
||||
p => string.Equals(p.Name, o.Name, StringComparison.OrdinalIgnoreCase));
|
||||
if (existing >= 0)
|
||||
{
|
||||
properties[existing] = o;
|
||||
}
|
||||
else
|
||||
{
|
||||
properties.Add(o);
|
||||
}
|
||||
}
|
||||
|
||||
return WorkflowExpr.Object(properties);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// PAYLOAD BUILDERS
|
||||
// Reusable expressions for common service call payloads.
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
internal static WorkflowExpressionDefinition BuildAlterPolicyPayload()
|
||||
{
|
||||
return WorkflowExpr.Object(
|
||||
WorkflowExpr.Prop("srPolicyId", WorkflowExpr.Path("state.srPolicyId")),
|
||||
WorkflowExpr.Prop("beginDate", WorkflowExpr.Path("state.beginDate")),
|
||||
WorkflowExpr.Prop("endDate", WorkflowExpr.Path("state.endDate")),
|
||||
WorkflowExpr.Prop("annexType", WorkflowExpr.Path("state.annexType")));
|
||||
}
|
||||
|
||||
internal static WorkflowExpressionDefinition BuildAnnexTypeEquals(string type)
|
||||
{
|
||||
return WorkflowExpr.Eq(
|
||||
WorkflowExpr.Func("upper", WorkflowExpr.Path("state.annexType")),
|
||||
WorkflowExpr.String(type));
|
||||
}
|
||||
|
||||
internal static WorkflowExpressionDefinition BuildPolicyIdPayload()
|
||||
{
|
||||
return WorkflowExpr.Object(
|
||||
WorkflowExpr.Prop("srPolicyId", WorkflowExpr.Path("state.srPolicyId")));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// WORKFLOW INVOCATION BUILDERS
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
internal static WorkflowWorkflowInvocationDeclaration BuildReviewInvocation()
|
||||
{
|
||||
return new WorkflowWorkflowInvocationDeclaration
|
||||
{
|
||||
WorkflowName = ReviewPolicyReference.WorkflowName,
|
||||
PayloadExpression = WorkflowExpr.Path("state"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for common flow patterns.
|
||||
/// Used across multiple workflows for DRY step sequences.
|
||||
/// </summary>
|
||||
internal static class PolicyWorkflowFlowExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies product info from a service call result into workflow state.
|
||||
/// </summary>
|
||||
internal static WorkflowFlowBuilder<T> ApplyProductInfo<T>(
|
||||
this WorkflowFlowBuilder<T> flow,
|
||||
string resultKey = "productInfo")
|
||||
where T : class
|
||||
{
|
||||
return flow
|
||||
.SetIfHasValue("productCode",
|
||||
WorkflowExpr.Func("upper",
|
||||
WorkflowExpr.Path($"result.{resultKey}.productCode")))
|
||||
.SetIfHasValue("lob",
|
||||
WorkflowExpr.Func("upper",
|
||||
WorkflowExpr.Path($"result.{resultKey}.lob")))
|
||||
.SetIfHasValue("contractType",
|
||||
WorkflowExpr.Path($"result.{resultKey}.contractType"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard "load product info and apply" pattern.
|
||||
/// </summary>
|
||||
internal static WorkflowFlowBuilder<T> LoadAndApplyProductInfo<T>(
|
||||
this WorkflowFlowBuilder<T> flow)
|
||||
where T : class
|
||||
{
|
||||
return flow
|
||||
.Call<object>("Load Product Info",
|
||||
Address.LegacyRabbit("pas_get_policy_product_info"),
|
||||
PolicyWorkflowSupport.BuildPolicyIdPayload(),
|
||||
WorkflowHandledBranchAction.Complete,
|
||||
WorkflowHandledBranchAction.Complete,
|
||||
resultKey: "productInfo")
|
||||
.ApplyProductInfo();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user