Skip to content

Commit ca1b334

Browse files
authored
feat: Wire Starlark handler registry in services (#717)
* feat: Wire Starlark handler registry in current-account and payment-order services Add HandlerRegistry initialization with service client handlers in both current-account and payment-order services, enabling saga scripts to call real gRPC services instead of mocks. Current-account service: - Creates HandlerRegistry populated with position-keeping, financial-accounting, and current-account handlers via RegisterStarlarkHandlers - Handlers adapt Starlark params (map[string]any) to gRPC calls with saga metadata propagation (idempotency, correlation, etc.) - Registry is stored in serviceClients for future SagaExecutor integration Payment-order service: - Creates HandlerRegistry with position-keeping, financial-accounting, and current-account handlers - Uses type assertions for clients stored as interfaces - Registry available for when payment execution sagas migrate to Starlark This completes the handler wiring per starlark-service-bindings.md PRD. The Go orchestrators (DepositOrchestrator, PaymentOrchestrator) remain functional - Starlark migration is a future task. saga-script-versioning.13 * fix: Downgrade handler registration failures to warnings in payment-order Handler registration failures are now warnings instead of errors since the Starlark runner isn't wired yet. This prevents startup failures when position-keeping is unavailable during the migration period. Changes: - Position-keeping client creation failure is now a warning - Handler registration errors are now warnings (not startup failures) - Nil check for posKeepingClient before calling RegisterStarlarkHandlers - Proper defer cleanup only when client is successfully created The service can now start and operate without Starlark handlers until the full Starlark migration is complete. saga-script-versioning.13 --------- Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent f6df7b3 commit ca1b334

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

services/current-account/cmd/main.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
pb "github.com/meridianhub/meridian/api/proto/meridian/current_account/v1"
1414
"github.com/meridianhub/meridian/services/current-account/adapters/persistence"
15+
currentaccountclient "github.com/meridianhub/meridian/services/current-account/client"
1516
"github.com/meridianhub/meridian/services/current-account/config"
1617
caobservability "github.com/meridianhub/meridian/services/current-account/observability"
1718
"github.com/meridianhub/meridian/services/current-account/service"
@@ -22,6 +23,7 @@ import (
2223
refdataclient "github.com/meridianhub/meridian/services/reference-data/client"
2324
sharedclients "github.com/meridianhub/meridian/shared/pkg/clients"
2425
"github.com/meridianhub/meridian/shared/pkg/idempotency"
26+
"github.com/meridianhub/meridian/shared/pkg/saga"
2527
"github.com/meridianhub/meridian/shared/platform/bootstrap"
2628
"github.com/meridianhub/meridian/shared/platform/defaults"
2729
"github.com/meridianhub/meridian/shared/platform/env"
@@ -241,6 +243,10 @@ type serviceClients struct {
241243
// Health clients bypass the circuit breaker for health checks
242244
positionKeepingHealth grpc_health_v1.HealthClient
243245
financialAccountingHealth grpc_health_v1.HealthClient
246+
// HandlerRegistry for Starlark saga execution with service client handlers.
247+
// This registry contains handlers that call real gRPC services (not mocks).
248+
// See PRD: docs/prd/starlark-service-bindings.md
249+
handlerRegistry *saga.HandlerRegistry
244250
// Cleanup functions for graceful shutdown
245251
cleanupFuncs []func()
246252
}
@@ -441,6 +447,53 @@ func createServiceWithClients(
441447
return nil, nil, fmt.Errorf("failed to create service with existing clients: %w", err)
442448
}
443449

450+
// Create Starlark handler registry with service client handlers.
451+
// This enables saga scripts to call real services (not mocks).
452+
// See PRD: docs/prd/starlark-service-bindings.md
453+
handlerRegistry := saga.NewHandlerRegistry()
454+
455+
// Register handlers from service clients.
456+
// Each RegisterStarlarkHandlers function adapts Starlark params (map[string]any)
457+
// to gRPC client calls, propagating saga metadata (idempotency, correlation, etc.)
458+
if err := poskeepingclient.RegisterStarlarkHandlers(handlerRegistry, posKeepingClient); err != nil {
459+
for _, cleanup := range cleanupFuncs {
460+
cleanup()
461+
}
462+
return nil, nil, fmt.Errorf("failed to register position-keeping handlers: %w", err)
463+
}
464+
465+
if err := finacctclient.RegisterStarlarkHandlers(handlerRegistry, finAcctClient); err != nil {
466+
for _, cleanup := range cleanupFuncs {
467+
cleanup()
468+
}
469+
return nil, nil, fmt.Errorf("failed to register financial-accounting handlers: %w", err)
470+
}
471+
472+
// Register current-account handlers (for self-referential operations in sagas)
473+
// Note: We need a current-account client that connects to this service's gRPC endpoint.
474+
// For now, we create one using the same namespace/port configuration.
475+
currentAcctClient, currentAcctCleanup, err := currentaccountclient.New(currentaccountclient.Config{
476+
ServiceName: currentaccountclient.ServiceName,
477+
Namespace: namespace,
478+
Port: ports.CurrentAccount,
479+
Timeout: defaults.DefaultRPCTimeout,
480+
Tracer: tracer,
481+
// No resilience for self-calls - we want fast failure for local issues
482+
})
483+
if err != nil {
484+
// Current-account handlers are optional - service can work without them
485+
logger.Warn("failed to create current-account client for Starlark handlers",
486+
"error", err)
487+
} else {
488+
cleanupFuncs = append(cleanupFuncs, currentAcctCleanup)
489+
if regErr := currentaccountclient.RegisterStarlarkHandlers(handlerRegistry, currentAcctClient); regErr != nil {
490+
logger.Warn("failed to register current-account handlers", "error", regErr)
491+
}
492+
}
493+
494+
logger.Info("Starlark handler registry initialized",
495+
"registered_handlers", len(handlerRegistry.List()))
496+
444497
// Create health clients from the underlying gRPC connections
445498
// These bypass the circuit breaker to avoid health checks tripping business operation circuit breakers
446499
svcClients := &serviceClients{
@@ -451,6 +504,7 @@ func createServiceWithClients(
451504
accountResolver: accountResolver,
452505
positionKeepingHealth: grpc_health_v1.NewHealthClient(posKeepingClient.Conn()),
453506
financialAccountingHealth: grpc_health_v1.NewHealthClient(finAcctClient.Conn()),
507+
handlerRegistry: handlerRegistry,
454508
cleanupFuncs: cleanupFuncs,
455509
}
456510

services/payment-order/cmd/main.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
currentaccountclient "github.com/meridianhub/meridian/services/current-account/client"
3434
financialaccountingclient "github.com/meridianhub/meridian/services/financial-accounting/client"
3535
internalbankaccountclient "github.com/meridianhub/meridian/services/internal-bank-account/client"
36+
positionkeepingclient "github.com/meridianhub/meridian/services/position-keeping/client"
37+
"github.com/meridianhub/meridian/shared/pkg/saga"
3638
)
3739

3840
// Build information set via ldflags during compilation.
@@ -155,6 +157,70 @@ func run(logger *slog.Logger) error {
155157
}()
156158
idempotencyService := idempotency.NewRedisService(redisClient)
157159

160+
// Create Starlark handler registry with service client handlers.
161+
// This enables saga scripts to call real services (not mocks).
162+
// See PRD: docs/prd/starlark-service-bindings.md
163+
handlerRegistry := saga.NewHandlerRegistry()
164+
165+
// Create position-keeping client for Starlark handlers.
166+
// Note: payment-order needs position-keeping for payment execution sagas.
167+
// This is optional during migration - service can start without it until Starlark is active.
168+
posKeepingClient, posKeepingCleanup, err := positionkeepingclient.New(positionkeepingclient.Config{
169+
ServiceName: positionkeepingclient.ServiceName,
170+
Namespace: namespace,
171+
Port: ports.PositionKeeping,
172+
Timeout: defaults.DefaultRPCTimeout,
173+
Tracer: tracer,
174+
// No circuit breaker for saga handlers - saga has its own retry logic
175+
})
176+
if err != nil {
177+
// Downgrade to warning - Starlark runtime isn't wired yet, service can operate without handlers
178+
logger.Warn("position-keeping client unavailable, Starlark handlers not registered",
179+
"error", err)
180+
} else {
181+
defer posKeepingCleanup()
182+
}
183+
184+
// Register handlers from service clients.
185+
// Each RegisterStarlarkHandlers function adapts Starlark params (map[string]any)
186+
// to gRPC client calls, propagating saga metadata (idempotency, correlation, etc.)
187+
//
188+
// Note: Type assertions are required because the createXxxClient functions return
189+
// interface types (service.XxxClient) but RegisterStarlarkHandlers needs the
190+
// concrete *Client type to access the gRPC connection.
191+
//
192+
// Handler registration failures are warnings (not errors) since Starlark runner
193+
// isn't wired yet - service can operate without handlers during migration.
194+
if caClient, ok := currentAccountClient.(*currentaccountclient.Client); ok {
195+
if err := currentaccountclient.RegisterStarlarkHandlers(handlerRegistry, caClient); err != nil {
196+
logger.Warn("failed to register current-account handlers", "error", err)
197+
}
198+
} else {
199+
logger.Warn("current-account client type assertion failed, Starlark handlers not registered")
200+
}
201+
202+
if faClient, ok := financialAccountingClient.(*financialaccountingclient.Client); ok {
203+
if err := financialaccountingclient.RegisterStarlarkHandlers(handlerRegistry, faClient); err != nil {
204+
logger.Warn("failed to register financial-accounting handlers", "error", err)
205+
}
206+
} else {
207+
logger.Warn("financial-accounting client type assertion failed, Starlark handlers not registered")
208+
}
209+
210+
if posKeepingClient != nil {
211+
if err := positionkeepingclient.RegisterStarlarkHandlers(handlerRegistry, posKeepingClient); err != nil {
212+
logger.Warn("failed to register position-keeping handlers", "error", err)
213+
}
214+
}
215+
216+
logger.Info("Starlark handler registry initialized",
217+
"registered_handlers", len(handlerRegistry.List()))
218+
219+
// Note: handlerRegistry is available for use with StarlarkRunner when
220+
// payment execution sagas are migrated from Go orchestrators to Starlark.
221+
// See task 13 in saga-script-versioning for migration details.
222+
_ = handlerRegistry // TODO: Wire to StarlarkRunner when saga migration completes
223+
158224
// Create payment order service
159225
paymentOrderService, err := service.NewServiceWithConfig(service.Config{
160226
Repository: repo,

0 commit comments

Comments
 (0)