59 lines
1.8 KiB
Go
59 lines
1.8 KiB
Go
package order
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"git.k6n.net/mats/go-cart-actor/pkg/flow"
|
|
)
|
|
|
|
// Publisher is the minimal seam the amqp_emit hook needs. cmd/order supplies a
|
|
// RabbitMQ-backed implementation; tests pass a fake. Keeping it an interface
|
|
// keeps pkg/order free of an AMQP dependency.
|
|
type Publisher interface {
|
|
Publish(exchange, routingKey string, body []byte) error
|
|
}
|
|
|
|
// RegisterEmitHook registers the "amqp_emit" flow hook, which publishes a flow
|
|
// event to a message broker — the bridge from a saga step to other services
|
|
// (loyalty, ERP sync, notifications) without coupling the flow to them. It is
|
|
// the commerce analogue of the CMS event listeners. Params (optional):
|
|
//
|
|
// {"exchange": "", "routingKey": "order-events"}
|
|
//
|
|
// The body is {step, phase, id, error, vars}. With a nil publisher the hook is
|
|
// still registered (so the editor lists it) but errors at run time — and since
|
|
// hook errors never abort a flow, that failure is logged, not fatal.
|
|
func RegisterEmitHook(reg *flow.Registry, pub Publisher) {
|
|
reg.Hook("amqp_emit", func(_ context.Context, st *flow.State, info flow.HookInfo, params json.RawMessage) error {
|
|
if pub == nil {
|
|
return fmt.Errorf("amqp_emit: no publisher configured")
|
|
}
|
|
var p struct {
|
|
Exchange string `json:"exchange"`
|
|
RoutingKey string `json:"routingKey"`
|
|
}
|
|
if len(params) > 0 {
|
|
if err := json.Unmarshal(params, &p); err != nil {
|
|
return fmt.Errorf("amqp_emit: bad params: %w", err)
|
|
}
|
|
}
|
|
if p.RoutingKey == "" {
|
|
p.RoutingKey = "order-events"
|
|
}
|
|
errStr := ""
|
|
if info.Err != nil {
|
|
errStr = info.Err.Error()
|
|
}
|
|
body, _ := json.Marshal(map[string]any{
|
|
"step": info.Step,
|
|
"phase": info.Phase,
|
|
"id": st.ID,
|
|
"error": errStr,
|
|
"vars": st.Vars,
|
|
})
|
|
return pub.Publish(p.Exchange, p.RoutingKey, body)
|
|
})
|
|
}
|