Skip to content

Commit f83e07e

Browse files
authored
refactor: migrate 11 client packages to shared gRPC client factory (#2019)
Replace duplicated connection setup logic in each service client's New() function with the shared clients.NewConn() factory from task 25. This removes ~570 lines of boilerplate across current-account, financial- accounting, internal-account, party, position-keeping, tenant, reconciliation, market-information, operational-gateway, financial- gateway, and reference-data client packages. Each client's ErrTargetRequired now aliases clients.ErrConnTargetRequired for backward compatibility with existing tests. Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent be49936 commit f83e07e

11 files changed

Lines changed: 136 additions & 704 deletions

File tree

services/current-account/client/client.go

Lines changed: 13 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,15 @@ package client
2828

2929
import (
3030
"context"
31-
"errors"
3231
"fmt"
3332
"time"
3433

3534
currentaccountv1 "github.com/meridianhub/meridian/api/proto/meridian/current_account/v1"
3635
"github.com/meridianhub/meridian/shared/pkg/clients"
37-
platformgrpc "github.com/meridianhub/meridian/shared/pkg/grpc"
3836
"github.com/meridianhub/meridian/shared/pkg/validation"
3937
"github.com/meridianhub/meridian/shared/platform/observability"
4038
"google.golang.org/grpc"
4139
"google.golang.org/grpc/codes"
42-
"google.golang.org/grpc/credentials/insecure"
4340
"google.golang.org/grpc/status"
4441
)
4542

@@ -58,7 +55,7 @@ const (
5855
)
5956

6057
// ErrTargetRequired is returned when neither Target nor ServiceName is provided.
61-
var ErrTargetRequired = errors.New("either Target or ServiceName must be provided")
58+
var ErrTargetRequired = clients.ErrConnTargetRequired
6259

6360
// Config holds configuration for the CurrentAccount client.
6461
type Config struct {
@@ -122,7 +119,6 @@ type Client struct {
122119
// }
123120
// defer cleanup()
124121
func New(cfg Config) (*Client, func(), error) {
125-
// Apply defaults
126122
if cfg.Timeout == 0 {
127123
cfg.Timeout = DefaultTimeout
128124
}
@@ -133,81 +129,30 @@ func New(cfg Config) (*Client, func(), error) {
133129
cfg.Namespace = DefaultNamespace
134130
}
135131

136-
var conn *grpc.ClientConn
137-
var err error
138-
139-
// Use platform gRPC factory when ServiceName is provided (preferred)
140-
if cfg.ServiceName != "" {
141-
dialOpts := cfg.DialOptions
142-
143-
// Add tracing interceptors if tracer is provided
144-
// Use WithChainUnaryInterceptor/WithChainStreamInterceptor to properly chain
145-
// multiple interceptors instead of overwriting them
146-
if cfg.Tracer != nil {
147-
dialOpts = append(dialOpts,
148-
grpc.WithChainUnaryInterceptor(cfg.Tracer.UnaryClientInterceptor()),
149-
grpc.WithChainStreamInterceptor(cfg.Tracer.StreamClientInterceptor()),
150-
)
151-
}
152-
153-
// Use platform factory for DNS-based load balancing
154-
conn, err = platformgrpc.NewClient(context.Background(), platformgrpc.ClientConfig{
155-
ServiceName: cfg.ServiceName,
156-
Namespace: cfg.Namespace,
157-
Port: cfg.Port,
158-
DialOptions: dialOpts,
159-
})
160-
if err != nil {
161-
return nil, nil, fmt.Errorf("failed to create current-account gRPC connection via platform factory: %w", err)
162-
}
163-
} else if cfg.Target != "" {
164-
// Fallback to legacy direct connection for backward compatibility
165-
dialOpts := cfg.DialOptions
166-
if dialOpts == nil {
167-
dialOpts = []grpc.DialOption{
168-
grpc.WithTransportCredentials(insecure.NewCredentials()),
169-
}
170-
}
171-
172-
// Add tracing interceptors if tracer is provided
173-
// Use WithChainUnaryInterceptor/WithChainStreamInterceptor to properly chain
174-
// multiple interceptors instead of overwriting them
175-
if cfg.Tracer != nil {
176-
dialOpts = append(dialOpts,
177-
grpc.WithChainUnaryInterceptor(cfg.Tracer.UnaryClientInterceptor()),
178-
grpc.WithChainStreamInterceptor(cfg.Tracer.StreamClientInterceptor()),
179-
)
180-
}
181-
182-
conn, err = grpc.NewClient(cfg.Target, dialOpts...)
183-
if err != nil {
184-
return nil, nil, fmt.Errorf("failed to create current-account gRPC connection to %s: %w", cfg.Target, err)
185-
}
186-
} else {
187-
return nil, nil, ErrTargetRequired
132+
conn, cleanup, err := clients.NewConn(context.Background(), clients.ConnConfig{
133+
Target: cfg.Target,
134+
ServiceName: cfg.ServiceName,
135+
Namespace: cfg.Namespace,
136+
Port: cfg.Port,
137+
Tracer: cfg.Tracer,
138+
DialOptions: cfg.DialOptions,
139+
})
140+
if err != nil {
141+
return nil, nil, err
188142
}
189143

190-
// Create resilient client if configuration is provided
191144
var resilient *clients.ResilientClient
192145
if cfg.Resilience != nil {
193146
resilient = clients.NewResilientClient(*cfg.Resilience)
194147
}
195148

196-
client := &Client{
149+
return &Client{
197150
conn: conn,
198151
currentAccount: currentaccountv1.NewCurrentAccountServiceClient(conn),
199152
tracer: cfg.Tracer,
200153
resilient: resilient,
201154
timeout: cfg.Timeout,
202-
}
203-
204-
cleanup := func() {
205-
if client.conn != nil {
206-
_ = client.conn.Close()
207-
}
208-
}
209-
210-
return client, cleanup, nil
155+
}, cleanup, nil
211156
}
212157

213158
// InitiateCurrentAccount creates a new current account facility.

services/financial-accounting/client/client.go

Lines changed: 13 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,13 @@ package client
2727

2828
import (
2929
"context"
30-
"errors"
3130
"fmt"
3231
"time"
3332

3433
financialaccountingv1 "github.com/meridianhub/meridian/api/proto/meridian/financial_accounting/v1"
3534
"github.com/meridianhub/meridian/shared/pkg/clients"
36-
platformgrpc "github.com/meridianhub/meridian/shared/pkg/grpc"
3735
"github.com/meridianhub/meridian/shared/platform/observability"
3836
"google.golang.org/grpc"
39-
"google.golang.org/grpc/credentials/insecure"
4037
)
4138

4239
const (
@@ -54,7 +51,7 @@ const (
5451
)
5552

5653
// ErrTargetRequired is returned when neither Target nor ServiceName is provided.
57-
var ErrTargetRequired = errors.New("either Target or ServiceName must be provided")
54+
var ErrTargetRequired = clients.ErrConnTargetRequired
5855

5956
// Config holds configuration for the FinancialAccounting client.
6057
type Config struct {
@@ -118,7 +115,6 @@ type Client struct {
118115
// }
119116
// defer cleanup()
120117
func New(ctx context.Context, cfg Config) (*Client, func(), error) {
121-
// Apply defaults
122118
if cfg.Timeout == 0 {
123119
cfg.Timeout = DefaultTimeout
124120
}
@@ -129,81 +125,30 @@ func New(ctx context.Context, cfg Config) (*Client, func(), error) {
129125
cfg.Namespace = DefaultNamespace
130126
}
131127

132-
var conn *grpc.ClientConn
133-
var err error
134-
135-
// Use platform gRPC factory when ServiceName is provided (preferred)
136-
if cfg.ServiceName != "" {
137-
dialOpts := cfg.DialOptions
138-
139-
// Add tracing interceptors if tracer is provided
140-
// Use WithChainUnaryInterceptor/WithChainStreamInterceptor to properly chain
141-
// multiple interceptors instead of overwriting them
142-
if cfg.Tracer != nil {
143-
dialOpts = append(dialOpts,
144-
grpc.WithChainUnaryInterceptor(cfg.Tracer.UnaryClientInterceptor()),
145-
grpc.WithChainStreamInterceptor(cfg.Tracer.StreamClientInterceptor()),
146-
)
147-
}
148-
149-
// Use platform factory for DNS-based load balancing
150-
conn, err = platformgrpc.NewClient(ctx, platformgrpc.ClientConfig{
151-
ServiceName: cfg.ServiceName,
152-
Namespace: cfg.Namespace,
153-
Port: cfg.Port,
154-
DialOptions: dialOpts,
155-
})
156-
if err != nil {
157-
return nil, nil, fmt.Errorf("failed to create financial-accounting gRPC connection via platform factory: %w", err)
158-
}
159-
} else if cfg.Target != "" {
160-
// Fallback to legacy direct connection for backward compatibility
161-
dialOpts := cfg.DialOptions
162-
if dialOpts == nil {
163-
dialOpts = []grpc.DialOption{
164-
grpc.WithTransportCredentials(insecure.NewCredentials()),
165-
}
166-
}
167-
168-
// Add tracing interceptors if tracer is provided
169-
// Use WithChainUnaryInterceptor/WithChainStreamInterceptor to properly chain
170-
// multiple interceptors instead of overwriting them
171-
if cfg.Tracer != nil {
172-
dialOpts = append(dialOpts,
173-
grpc.WithChainUnaryInterceptor(cfg.Tracer.UnaryClientInterceptor()),
174-
grpc.WithChainStreamInterceptor(cfg.Tracer.StreamClientInterceptor()),
175-
)
176-
}
177-
178-
conn, err = grpc.NewClient(cfg.Target, dialOpts...)
179-
if err != nil {
180-
return nil, nil, fmt.Errorf("failed to create financial-accounting gRPC connection to %s: %w", cfg.Target, err)
181-
}
182-
} else {
183-
return nil, nil, ErrTargetRequired
128+
conn, cleanup, err := clients.NewConn(ctx, clients.ConnConfig{
129+
Target: cfg.Target,
130+
ServiceName: cfg.ServiceName,
131+
Namespace: cfg.Namespace,
132+
Port: cfg.Port,
133+
Tracer: cfg.Tracer,
134+
DialOptions: cfg.DialOptions,
135+
})
136+
if err != nil {
137+
return nil, nil, err
184138
}
185139

186-
// Create resilient client if configuration is provided
187140
var resilient *clients.ResilientClient
188141
if cfg.Resilience != nil {
189142
resilient = clients.NewResilientClient(*cfg.Resilience)
190143
}
191144

192-
client := &Client{
145+
return &Client{
193146
conn: conn,
194147
financialAccounting: financialaccountingv1.NewFinancialAccountingServiceClient(conn),
195148
tracer: cfg.Tracer,
196149
resilient: resilient,
197150
timeout: cfg.Timeout,
198-
}
199-
200-
cleanup := func() {
201-
if client.conn != nil {
202-
_ = client.conn.Close()
203-
}
204-
}
205-
206-
return client, cleanup, nil
151+
}, cleanup, nil
207152
}
208153

209154
// InitiateFinancialBookingLog creates a new financial booking log.

services/financial-gateway/client/client.go

Lines changed: 13 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,13 @@ package client
2727

2828
import (
2929
"context"
30-
"errors"
3130
"fmt"
3231
"time"
3332

3433
financialgatewayv1 "github.com/meridianhub/meridian/api/proto/meridian/financial_gateway/v1"
3534
"github.com/meridianhub/meridian/shared/pkg/clients"
36-
platformgrpc "github.com/meridianhub/meridian/shared/pkg/grpc"
3735
"github.com/meridianhub/meridian/shared/platform/observability"
3836
"google.golang.org/grpc"
39-
"google.golang.org/grpc/credentials/insecure"
4037
)
4138

4239
const (
@@ -54,7 +51,7 @@ const (
5451
)
5552

5653
// ErrTargetRequired is returned when neither Target nor ServiceName is provided.
57-
var ErrTargetRequired = errors.New("either Target or ServiceName must be provided")
54+
var ErrTargetRequired = clients.ErrConnTargetRequired
5855

5956
// Config holds configuration for the FinancialGateway client.
6057
type Config struct {
@@ -106,7 +103,6 @@ type Client struct {
106103
// Returns the client, a cleanup function to close the connection, and any error.
107104
// The cleanup function should be deferred immediately after checking the error.
108105
func New(cfg Config) (*Client, func(), error) {
109-
// Apply defaults
110106
if cfg.Timeout == 0 {
111107
cfg.Timeout = DefaultTimeout
112108
}
@@ -117,73 +113,30 @@ func New(cfg Config) (*Client, func(), error) {
117113
cfg.Namespace = DefaultNamespace
118114
}
119115

120-
var conn *grpc.ClientConn
121-
var err error
122-
123-
// Use platform gRPC factory when ServiceName is provided (preferred)
124-
if cfg.ServiceName != "" {
125-
dialOpts := cfg.DialOptions
126-
127-
if cfg.Tracer != nil {
128-
dialOpts = append(dialOpts,
129-
grpc.WithChainUnaryInterceptor(cfg.Tracer.UnaryClientInterceptor()),
130-
grpc.WithChainStreamInterceptor(cfg.Tracer.StreamClientInterceptor()),
131-
)
132-
}
133-
134-
conn, err = platformgrpc.NewClient(context.Background(), platformgrpc.ClientConfig{
135-
ServiceName: cfg.ServiceName,
136-
Namespace: cfg.Namespace,
137-
Port: cfg.Port,
138-
DialOptions: dialOpts,
139-
})
140-
if err != nil {
141-
return nil, nil, fmt.Errorf("failed to create financial-gateway gRPC connection via platform factory: %w", err)
142-
}
143-
} else if cfg.Target != "" {
144-
dialOpts := cfg.DialOptions
145-
if dialOpts == nil {
146-
dialOpts = []grpc.DialOption{
147-
grpc.WithTransportCredentials(insecure.NewCredentials()),
148-
}
149-
}
150-
151-
if cfg.Tracer != nil {
152-
dialOpts = append(dialOpts,
153-
grpc.WithChainUnaryInterceptor(cfg.Tracer.UnaryClientInterceptor()),
154-
grpc.WithChainStreamInterceptor(cfg.Tracer.StreamClientInterceptor()),
155-
)
156-
}
157-
158-
conn, err = grpc.NewClient(cfg.Target, dialOpts...)
159-
if err != nil {
160-
return nil, nil, fmt.Errorf("failed to create financial-gateway gRPC connection to %s: %w", cfg.Target, err)
161-
}
162-
} else {
163-
return nil, nil, ErrTargetRequired
116+
conn, cleanup, err := clients.NewConn(context.Background(), clients.ConnConfig{
117+
Target: cfg.Target,
118+
ServiceName: cfg.ServiceName,
119+
Namespace: cfg.Namespace,
120+
Port: cfg.Port,
121+
Tracer: cfg.Tracer,
122+
DialOptions: cfg.DialOptions,
123+
})
124+
if err != nil {
125+
return nil, nil, err
164126
}
165127

166-
// Create resilient client if configuration is provided
167128
var resilient *clients.ResilientClient
168129
if cfg.Resilience != nil {
169130
resilient = clients.NewResilientClient(*cfg.Resilience)
170131
}
171132

172-
c := &Client{
133+
return &Client{
173134
conn: conn,
174135
financialGateway: financialgatewayv1.NewFinancialGatewayServiceClient(conn),
175136
tracer: cfg.Tracer,
176137
resilient: resilient,
177138
timeout: cfg.Timeout,
178-
}
179-
180-
cleanup := func() {
181-
if c.conn != nil {
182-
_ = c.conn.Close()
183-
}
184-
}
185-
186-
return c, cleanup, nil
139+
}, cleanup, nil
187140
}
188141

189142
// DispatchPayment submits a payment for outbound dispatch via a payment rail.

0 commit comments

Comments
 (0)