Skip to content

Commit 24d29af

Browse files
authored
Merge pull request #275 from xmidt-org/denopink/feat/fx-integration
feat: integrate uber fx
2 parents 55bf050 + d74d651 commit 24d29af

17 files changed

+415
-452
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
13+
*.html
1314
coverage*
1415
report.json
1516

anclafx/provide.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-FileCopyrightText: 2025 Comcast Cable Communications Management, LLC
2+
// SPDX-License-Identifier: Apache-2.0
3+
package anclafx
4+
5+
import (
6+
"github.com/xmidt-org/ancla"
7+
"github.com/xmidt-org/ancla/chrysom"
8+
"go.uber.org/fx"
9+
)
10+
11+
const Module = "ancla"
12+
13+
func Provide() fx.Option {
14+
return fx.Module(
15+
Module,
16+
fx.Invoke(chrysom.ProvideStartListenerClient),
17+
fx.Provide(
18+
ancla.ProvideService,
19+
ancla.ProvideListener,
20+
ancla.ProvideDefaultListenerWatchers,
21+
chrysom.ProvideBasicClient,
22+
chrysom.ProvideDefaultListenerReader,
23+
chrysom.ProvideListenerClient,
24+
),
25+
chrysom.ProvideMetrics(),
26+
)
27+
}

anclafx/provide_test.go

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// SPDX-FileCopyrightText: 2025 Comcast Cable Communications Management, LLC
2+
// SPDX-License-Identifier: Apache-2.0
3+
package anclafx_test
4+
5+
import (
6+
"context"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
"github.com/xmidt-org/ancla"
11+
"github.com/xmidt-org/ancla/anclafx"
12+
"github.com/xmidt-org/ancla/chrysom"
13+
"github.com/xmidt-org/touchstone"
14+
"go.uber.org/fx"
15+
"go.uber.org/fx/fxtest"
16+
"go.uber.org/zap"
17+
)
18+
19+
type out struct {
20+
fx.Out
21+
22+
Factory *touchstone.Factory
23+
BasicClientConfig chrysom.BasicClientConfig
24+
GetLogger chrysom.GetLogger
25+
SetLogger chrysom.SetLogger
26+
}
27+
28+
func provideDefaults() (out, error) {
29+
cfg := touchstone.Config{
30+
DefaultNamespace: "n",
31+
DefaultSubsystem: "s",
32+
}
33+
_, pr, err := touchstone.New(cfg)
34+
if err != nil {
35+
return out{}, err
36+
}
37+
38+
return out{
39+
Factory: touchstone.NewFactory(cfg, zap.NewNop(), pr),
40+
BasicClientConfig: chrysom.BasicClientConfig{
41+
Address: "example.com",
42+
Bucket: "bucket-name",
43+
},
44+
GetLogger: func(context.Context) *zap.Logger { return zap.NewNop() },
45+
SetLogger: func(context.Context, *zap.Logger) context.Context { return context.Background() },
46+
}, nil
47+
}
48+
49+
func TestProvide(t *testing.T) {
50+
t.Run("Test anclafx.Provide() defaults", func(t *testing.T) {
51+
var (
52+
svc ancla.Service
53+
bc *chrysom.BasicClient
54+
l *chrysom.ListenerClient
55+
)
56+
57+
app := fxtest.New(t,
58+
anclafx.Provide(),
59+
fx.Provide(
60+
provideDefaults,
61+
),
62+
fx.Populate(
63+
&svc,
64+
&bc,
65+
&l,
66+
),
67+
)
68+
69+
require := require.New(t)
70+
require.NotNil(app)
71+
require.NoError(app.Err())
72+
app.RequireStart()
73+
require.NotNil(svc)
74+
require.NotNil(bc)
75+
require.NotNil(l)
76+
app.RequireStop()
77+
})
78+
}

chrysom/basicClient.go

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"io"
1313
"net/http"
14+
"time"
1415

1516
"github.com/xmidt-org/ancla/auth"
1617
"github.com/xmidt-org/ancla/model"
@@ -41,6 +42,7 @@ var (
4142
errReadingBodyFailure = errors.New("failed while reading http response body")
4243
errJSONUnmarshal = errors.New("failed unmarshaling JSON response payload")
4344
errJSONMarshal = errors.New("failed marshaling item as JSON payload")
45+
errFailedConfig = errors.New("ancla configuration error")
4446
)
4547

4648
// BasicClientConfig contains config data for the client that will be used to
@@ -59,6 +61,10 @@ type BasicClientConfig struct {
5961
// Auth provides the mechanism to add auth headers to outgoing requests.
6062
// (Optional) If not provided, no auth headers are added.
6163
Auth auth.Decorator
64+
65+
// PullInterval is how often listeners should get updates.
66+
// (Optional). Defaults to 5 seconds.
67+
PullInterval time.Duration
6268
}
6369

6470
// BasicClient is the client used to make requests to Argus.

chrysom/basicClient_test.go

+7-11
Original file line numberDiff line numberDiff line change
@@ -54,34 +54,30 @@ func TestValidateBasicConfig(t *testing.T) {
5454
{
5555
Description: "No address",
5656
Input: &BasicClientConfig{
57-
HTTPClient: http.DefaultClient,
58-
Bucket: "bucket-name",
57+
Bucket: "bucket-name",
5958
},
6059
ExpectedErr: ErrAddressEmpty,
6160
},
6261
{
6362
Description: "No bucket",
6463
Input: &BasicClientConfig{
65-
HTTPClient: http.DefaultClient,
66-
Address: "example.com",
64+
Address: "example.com",
6765
},
6866
ExpectedErr: ErrBucketEmpty,
6967
},
7068
{
7169
Description: "All default values",
7270
Input: &BasicClientConfig{
73-
HTTPClient: http.DefaultClient,
74-
Address: "example.com",
75-
Bucket: "bucket-name",
71+
Address: "example.com",
72+
Bucket: "bucket-name",
7673
},
7774
ExpectedConfig: allDefaultsCaseConfig,
7875
},
7976
{
8077
Description: "All defined",
8178
Input: &BasicClientConfig{
82-
HTTPClient: http.DefaultClient,
83-
Address: "example.com",
84-
Bucket: "amazing-bucket",
79+
Address: "example.com",
80+
Bucket: "amazing-bucket",
8581
},
8682
ExpectedConfig: allDefinedCaseConfig,
8783
},
@@ -121,7 +117,7 @@ func TestSendRequest(t *testing.T) {
121117
ExpectedErr: errNewRequestFailure,
122118
},
123119
{
124-
Description: "Auth acquirer fails",
120+
Description: "Auth decorator fails",
125121
Method: http.MethodGet,
126122
URL: "example.com",
127123
MockError: errFails,

chrysom/fx.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-FileCopyrightText: 2025 Comcast Cable Communications Management, LLC
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package chrysom
5+
6+
import (
7+
"context"
8+
"errors"
9+
10+
"github.com/prometheus/client_golang/prometheus"
11+
"github.com/xmidt-org/touchstone"
12+
"go.uber.org/fx"
13+
"go.uber.org/zap"
14+
)
15+
16+
// GetLogger returns a logger from the given context.
17+
type GetLogger func(context.Context) *zap.Logger
18+
19+
// SetLogger embeds the `Listener.logger` in outgoing request contexts for `Listener.Update` calls.
20+
type SetLogger func(context.Context, *zap.Logger) context.Context
21+
22+
type BasicClientIn struct {
23+
fx.In
24+
25+
// Ancla Client config.
26+
Config BasicClientConfig
27+
// GetLogger returns a logger from the given context.
28+
GetLogger GetLogger
29+
}
30+
31+
// ProvideBasicClient provides a new BasicClient.
32+
func ProvideBasicClient(in BasicClientIn) (*BasicClient, error) {
33+
client, err := NewBasicClient(in.Config, in.GetLogger)
34+
if err != nil {
35+
return nil, errors.Join(errFailedConfig, err)
36+
}
37+
38+
return client, nil
39+
}
40+
41+
// ListenerConfig contains config data for polling the Argus client.
42+
type ListenerClientIn struct {
43+
fx.In
44+
45+
// Listener fetches a copy of all items within a bucket on
46+
// an interval based on `BasicClientConfig.PullInterval`.
47+
// (Optional). If not provided, listening won't be enabled for this client.
48+
Listener Listener
49+
// Config configures the ancla client and its listeners.
50+
Config BasicClientConfig
51+
// PollsTotalCounter measures the number of polls (and their success/failure outcomes) to fetch new items.
52+
PollsTotalCounter *prometheus.CounterVec `name:"chrysom_polls_total"`
53+
// Reader is the DB interface used to fetch new items using `GeItems`.
54+
Reader Reader
55+
// GetLogger returns a logger from the given context.
56+
GetLogger GetLogger
57+
// SetLogger embeds the `Listener.logger` in outgoing request contexts for `Listener.Update` calls.
58+
SetLogger SetLogger
59+
}
60+
61+
// ProvideListenerClient provides a new ListenerClient.
62+
func ProvideListenerClient(in ListenerClientIn) (*ListenerClient, error) {
63+
client, err := NewListenerClient(in.Listener, in.GetLogger, in.SetLogger, in.Config.PullInterval, in.PollsTotalCounter, in.Reader)
64+
if err != nil {
65+
return nil, errors.Join(err, errFailedConfig)
66+
}
67+
68+
return client, nil
69+
}
70+
71+
func ProvideDefaultListenerReader(client *BasicClient) Reader {
72+
return client
73+
}
74+
75+
type StartListenerIn struct {
76+
fx.In
77+
78+
Listener *ListenerClient
79+
LC fx.Lifecycle
80+
}
81+
82+
// ProvideStartListenerClient starts the Argus listener client service.
83+
func ProvideStartListenerClient(in StartListenerIn) error {
84+
in.Listener.Start(context.Background())
85+
in.LC.Append(fx.StopHook(in.Listener.Stop))
86+
87+
return nil
88+
}
89+
90+
func ProvideMetrics() fx.Option {
91+
return fx.Options(
92+
touchstone.Gauge(
93+
prometheus.GaugeOpts{
94+
Name: WebhookListSizeGaugeName,
95+
Help: WebhookListSizeGaugeHelp,
96+
}),
97+
touchstone.CounterVec(
98+
prometheus.CounterOpts{
99+
Name: PollsTotalCounterName,
100+
Help: PollsTotalCounterHelp,
101+
},
102+
OutcomeLabel,
103+
),
104+
)
105+
}

0 commit comments

Comments
 (0)