EIP processors (advanced)¶
pkg/processor/eip_advanced.go adds richer or more configurable variants of the patterns in eip.go: callback-driven enrichment and filtering, claim-check retrieval, data-driven normalizer and message router, stateful process manager, scatter-gather, messaging bridge, channel adapter, detour, structured logging, worker pool, rollback, stop, service call, and a stop-aware pipeline.
Most of these are Go-first APIs; some are exposed via the DSL (step, stop, rollback, threads, etc.).
Processors in this file¶
NewContentEnricher¶
Enriches an exchange by calling a user-supplied Go callback that returns extra data. Compare to EnrichProcessor which delegates to a producer/endpoint.
Constructor: NewContentEnricher(enrichFunc func(ctx, ex) (any, error), strategy engine.AggregationStrategy) *ContentEnricher.
| Field | Type | Default | Description |
|---|---|---|---|
EnrichFunc |
func(ctx, ex) (any, error) |
required | Returns enrichment data. |
Strategy |
engine.AggregationStrategy |
nil | If nil, the returned data replaces the body. Otherwise the strategy merges it with the original. |
NewContentFilter¶
The inverse of ContentEnricher — strips data from the body via a user callback. Use for PII removal, payload trimming, etc.
Constructor: NewContentFilter(filterFunc func(ctx, body any) (any, error)) *ContentFilter.
NewClaimCheckGet¶
A configurable variant of ClaimCheckOut: retrieve a stored payload by key, optionally remove the store entry, and optionally set the result as a header (HelixClaimCheckData) instead of replacing the body.
| Field | Type | Default | Description |
|---|---|---|---|
Store |
ClaimCheckStore |
required | Backing store (see eip.go). |
KeyExpression |
engine.Expression |
required | Computes the key. |
Remove |
bool | false |
Remove the entry after retrieval. |
Merge |
bool | false |
If true: set as header HelixClaimCheckData, keep current body. If false: replace body. |
Constructor: NewClaimCheckGet(store, keyExpr, remove bool) *ClaimCheckGet. Set Merge directly.
Normalizer¶
Data-driven variant of Normalize — rules can be added dynamically and there's an optional fallback.
n := processor.NewNormalizer(fallbackProc)
n.AddRule(predicateXML, xmlNormalizer)
n.AddRule(predicateJSON, jsonNormalizer)
Sets HelixNormalizerMatched to the index of the matched rule, or -1 if none matched.
Constructor: NewNormalizer(fallback engine.Processor) *Normalizer then AddRule(predicate, processor) for each rule.
MessageRouter¶
Data-driven variant of Choice. Named rules, optional fallback, and an AllMatch mode that runs every matching rule rather than just the first.
r := processor.NewMessageRouter(fallbackProc)
r.AddRule("vip", isVIP, vipPipeline)
r.AddRule("eu", isEU, euPipeline)
r.AllMatch = true // run every match
| Field | Type | Default | Description |
|---|---|---|---|
Rules |
[]RoutingRule |
empty | Predicate-processor pairs with names. |
Fallback |
engine.Processor |
nil | Runs when no rule matches. |
AllMatch |
bool | false |
If true: execute every matching rule. If false: first match wins. |
Sets HelixRouterRuleName to the matched rule (or "fallback").
ProcessManager¶
Stateful step-by-step routing — each step's transition function decides what runs next.
pm := processor.NewProcessManager("review")
pm.AddStep(&processor.ProcessStep{
Name: "review",
Processor: reviewProc,
Transition: func(ctx, ex) (string, error) {
if approved(ex) { return "ship", nil }
return "reject", nil
},
})
pm.AddStep(&processor.ProcessStep{Name: "ship", Processor: shipProc /* Transition: nil → end */ })
pm.AddStep(&processor.ProcessStep{Name: "reject", Processor: rejectProc})
| Field | Type | Default | Description |
|---|---|---|---|
Steps |
map[string]*ProcessStep |
empty | Named step lookup. |
InitialStep |
string | required | Where to start. |
MaxSteps |
int | 100 |
Safety cap; errors if exceeded. |
Sets HelixProcessManagerStep, HelixProcessManagerStepCount, HelixProcessManagerComplete. A nil transition ends the process normally.
ScatterGather¶
Combined Multicast + Aggregate. Sends copies in parallel, aggregates responses with a strategy, and supports a single gather-phase timeout.
| Field | Type | Default | Description |
|---|---|---|---|
Processors |
[]engine.Processor |
required | Recipients. |
Strategy |
engine.AggregationStrategy |
required | Merger. |
Timeout |
duration | 0 (no timeout) |
Maximum wait for all responses. On timeout, partial aggregation is kept and HelixScatterGatherTimeout = true is set. |
Sets HelixScatterGatherSize (recipient count) and HelixScatterGatherIndex on each copy.
Constructor: NewScatterGather(strategy engine.AggregationStrategy, timeout time.Duration, processors ...engine.Processor) *ScatterGather.
MessageBridge¶
Implements the Messaging Bridge EIP: in-processor → transformer → out-processor, with named source/target identifiers for tracing.
| Field | Type | Default | Description |
|---|---|---|---|
Source |
string | optional | Source name, set on HelixBridgeSource. |
Target |
string | optional | Target name, set on HelixBridgeTarget. |
InProcessor |
engine.Processor |
optional | Receives/transforms from source. |
Transformer |
engine.Processor |
optional | Format conversion between systems. |
OutProcessor |
engine.Processor |
optional | Sends to target. |
Constructor: NewMessageBridge(source, target string, inProc, transformer, outProc engine.Processor) *MessageBridge.
ChannelAdapter¶
Adapts a non-messaging data source (file pull, REST poll, DB query) into the exchange. The SourceFunc produces a value; the adapter writes it to the body and runs the downstream processor.
| Field | Type | Default | Description |
|---|---|---|---|
SourceName |
string | optional | Set on HelixChannelAdapterSource. |
SourceFunc |
func(ctx) (any, error) |
required | Produces data. |
Processor |
engine.Processor |
required | Downstream pipeline. |
Constructor: NewChannelAdapter(sourceName string, sourceFunc, processor) *ChannelAdapter.
Detour¶
Conditionally routes through an extra processor — useful for debug/audit injection without rewriting the main pipeline.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled |
engine.Predicate |
required | Activation condition. |
Detour |
engine.Processor |
required | Side processor. |
Processor |
engine.Processor |
optional | Main processor (when not intercepting). |
Intercept |
bool | false |
If true: detour replaces main when active. If false: detour runs first, then main. |
Sets HelixDetourEnabled to indicate activation.
LogEIP¶
Structured logging with optional inclusion of body/headers/properties, MDC (Mapped Diagnostic Context) keys, and custom log function.
| Field | Type | Default | Description |
|---|---|---|---|
Message |
string | required | Log message. |
Level |
slog.Level |
required | DEBUG/INFO/WARN/ERROR. |
LogBody |
bool | true (via constructor) |
Include body attr. |
LogHeaders |
bool | false |
Include headers attr. |
LogProperties |
bool | false |
Include properties attr. |
MDCKeys |
[]string |
empty | Property keys to include as top-level log attrs. |
Processor |
engine.Processor |
nil | Optional downstream — LogEIP then acts as a passthrough. |
CustomLogFunc |
func(ctx, ex, msg) |
nil | If set, replaces the default slog.Log path entirely. |
Sets HelixLogTimestamp = time.Now() before logging.
Threads¶
Worker-pool wrapper. Submits each call to a bounded semaphore pool; with WaitForSlot: true callers block until a slot is free, otherwise they fail fast with pool exhausted.
| Field | Type | Default | Description |
|---|---|---|---|
Processor |
engine.Processor |
required | The wrapped pipeline. |
PoolSize |
int | 10 |
Maximum concurrent in-flight processors. |
WaitForSlot |
bool | true |
Block (true) vs error (false) when the pool is full. |
Sets HelixThreadsPoolSize; sets HelixThreadsRejected = true if a non-waiting call was refused. Constructor: NewThreads(processor engine.Processor, poolSize int) *Threads.
Rollback¶
Marks the exchange for rollback. Sets HelixRollbackOnly = true and HelixRollbackMessage = <msg>. By default also calls ex.SetException(err) and returns the error so the surrounding pipeline aborts; MarkOnly: true skips setting the exception (useful for cooperative rollback from within a try-block).
Constructor: NewRollback(message string) *Rollback.
Stop¶
Halts routing for the current exchange. Sets HelixRouteStop = true and returns nil. The surrounding pipeline does not automatically honor this — wrap in StopAwarePipeline (or step, which the reifier wraps similarly in some contexts).
steps:
- choice:
when:
- starlang: header("X-Drop") == "1"
steps:
- stop: {}
- target: kafka://outbound
Constructor: NewStop() *Stop.
ServiceCall¶
Looks up a processor in the engine.Registry by service name and invokes it. Optionally runs a ServiceFilter to mutate the exchange first.
| Field | Type | Default | Description |
|---|---|---|---|
Registry |
engine.Registry |
required | Service lookup. |
ServiceName |
string | required | Logical service identifier. |
ServiceFilter |
engine.Processor |
nil | Optional pre-call mutator. |
Sets HelixServiceCallServiceName and HelixServiceCallServiceFound.
Constructor: NewServiceCall(registry engine.Registry, serviceName string) *ServiceCall.
StopAwarePipeline¶
A Pipeline that checks HelixRouteStop between processors and exits early when set. Use anywhere Stop should terminate the chain.
Constructor: NewStopAwarePipeline(processors ...engine.Processor) *StopAwarePipeline.
Pitfalls¶
MessageRouter.AllMatch = trueruns every matching rule — if rules are order-dependent, list them carefully.ScatterGatheraggregation does not depend on completion order; results are merged in receipt order from a channel, which is unordered. Use a strategy that's commutative, or post-process to re-order byHelixScatterGatherIndex.ThreadswithWaitForSlot: falsereturns an error on rejection — callers must handle this or wrap indo-try.Stoponly works if the surrounding pipeline isStopAwarePipeline; a plainPipelineignoresHelixRouteStop.
Examples¶
Stop with awareness in a normal Pipeline (use step:):
workflows:
- id: filtered-pipeline
trigger: { uri: direct://events }
steps:
- step:
id: filter-and-publish
steps:
- choice:
when:
- starlang: body().drop == true
steps:
- stop: {}
- target: kafka://events
Worker pool:
workflows:
- id: bounded-concurrency
trigger: { uri: kafka://heavy }
steps:
- threads:
pool-size: 16
steps:
- target: http://cpu-bound
Source¶
pkg/processor/eip_advanced.go
See also¶
- eip — base EIPs (Saga, ClaimCheck, Sort, Bean, SchemaValidator).
- eip_extra —
EnrichProcessor,PollEnrichProcessor,DelayProcessor,LoopProcessor,SampleProcessor, in-memory idempotent repo. - multicast —
MulticastandWireTap;ScatterGatherhere combines the patterns. - normalize — first-match-wins variant of
Normalizer. - choice — first-match-wins variant of
MessageRouter.