Skip to content

Commit

Permalink
feat: add options pattern to BasicClient
Browse files Browse the repository at this point in the history
- client options
  -  get logger
  - store base url
  - store api path
  - http client
  - store bucket
- listener options
  - set and get logger
  - pull interval
  - listeners (called at each pull interval)
- allow users to define their own db client instead of using argus
  • Loading branch information
denopink committed Jan 23, 2025
1 parent a72aed0 commit e8c26d7
Show file tree
Hide file tree
Showing 14 changed files with 863 additions and 435 deletions.
3 changes: 1 addition & 2 deletions anclafx/provide.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ func Provide() fx.Option {
ancla.ProvideService,
ancla.ProvideListener,
ancla.ProvideDefaultListenerWatchers,
chrysom.ProvideBasicClient,
chrysom.ProvideDefaultListenerReader,
chrysom.ProvideReaderOption,
chrysom.ProvideListenerClient,
),
chrysom.ProvideMetrics(),
Expand Down
39 changes: 21 additions & 18 deletions anclafx/provide_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ package anclafx_test

import (
"context"
"net/http"
"testing"
"time"

"github.com/stretchr/testify/require"
"github.com/xmidt-org/ancla"
Expand All @@ -19,9 +21,9 @@ import (
type out struct {
fx.Out

Factory *touchstone.Factory
BasicClientConfig chrysom.BasicClientConfig
Options chrysom.Options
Factory *touchstone.Factory
ClientOptions chrysom.ClientOptions `group:"client_options,flatten"`
ListenerOptions chrysom.ListenerOptions `group:"listener_options,flatten"`
}

func provideDefaults() (out, error) {
Expand All @@ -36,25 +38,26 @@ func provideDefaults() (out, error) {

return out{
Factory: touchstone.NewFactory(cfg, zap.NewNop(), pr),
BasicClientConfig: chrysom.BasicClientConfig{
Address: "example.com",
Bucket: "bucket-name",
},
GetLogger: func(context.Context) *zap.Logger { return zap.NewNop() },
SetLogger: func(context.Context, *zap.Logger) context.Context { return context.Background() },
Options: chrysom.Options{
chrysom.Address("example.com"),
ClientOptions: chrysom.ClientOptions{
chrysom.StoreBaseURL("example.com"),
chrysom.Bucket("bucket-name"),
chrysom.HTTPClient(http.DefaultClient),
chrysom.GetClientLogger(func(context.Context) *zap.Logger { return zap.NewNop() }),
},
ListenerOptions: chrysom.ListenerOptions{
chrysom.PullInterval(5 * time.Minute),
chrysom.GetListenerLogger(func(context.Context) *zap.Logger { return zap.NewNop() }),
chrysom.SetListenerLogger(func(context.Context, *zap.Logger) context.Context { return context.Background() }),
},
}, nil
}

func TestProvide(t *testing.T) {
t.Run("Test anclafx.Provide() defaults", func(t *testing.T) {
var (
svc ancla.Service
bc *chrysom.BasicClient
l *chrysom.ListenerClient
svc ancla.Service
reader chrysom.Reader
listener *chrysom.ListenerClient
)

app := fxtest.New(t,
Expand All @@ -64,8 +67,8 @@ func TestProvide(t *testing.T) {
),
fx.Populate(
&svc,
&bc,
&l,
&reader,
&listener,
),
)

Expand All @@ -74,8 +77,8 @@ func TestProvide(t *testing.T) {
require.NoError(app.Err())
app.RequireStart()
require.NotNil(svc)
require.NotNil(bc)
require.NotNil(l)
require.NotNil(reader)
require.NotNil(listener)
app.RequireStop()
})
}
37 changes: 37 additions & 0 deletions auth/context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2025 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0

package auth

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
)

func TestPrincipal(t *testing.T) {
t.Run("Test SetPartnerIDs, GetPartnerIDs", func(t *testing.T) {
assert := assert.New(t)
partnerIDs := []string{"foo", "bar"}
ctx := SetPartnerIDs(context.Background(), partnerIDs)
actualPartnerIDs, ok := GetPartnerIDs(ctx)
assert.True(ok)
assert.Equal(partnerIDs, actualPartnerIDs)
actualPartnerIDs, ok = GetPartnerIDs(context.Background())
assert.False(ok)
var empty []string
assert.Equal(empty, actualPartnerIDs)
})
t.Run("Test SetPrincipal, GetPrincipal", func(t *testing.T) {
assert := assert.New(t)
principal := "foo"
ctx := SetPrincipal(context.Background(), principal)
actualPrincipal, ok := GetPrincipal(ctx)
assert.True(ok)
assert.Equal(principal, actualPrincipal)
actualPrincipal, ok = GetPrincipal(context.Background())
assert.False(ok)
assert.Equal("", actualPrincipal)
})
}
75 changes: 16 additions & 59 deletions chrysom/basicClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import (
"fmt"
"io"
"net/http"
"time"

"github.com/xmidt-org/ancla/auth"
"github.com/xmidt-org/ancla/model"
"go.uber.org/fx"
"go.uber.org/zap"
)

Expand All @@ -26,50 +24,28 @@ const (
)

var (
ErrItemIDEmpty = errors.New("item ID is required")
ErrItemDataEmpty = errors.New("data field in item is required")
ErrUndefinedIntervalTicker = errors.New("interval ticker is nil. Can't listen for updates")
ErrAuthDecoratorFailure = errors.New("failed decorating auth header")
ErrBadRequest = errors.New("argus rejected the request as invalid")
ErrItemIDEmpty = errors.New("item ID is required")
ErrItemDataEmpty = errors.New("data field in item is required")
ErrAuthDecoratorFailure = errors.New("failed decorating auth header")
ErrBadRequest = errors.New("argus rejected the request as invalid")
)

var (
errNonSuccessResponse = errors.New("argus responded with a non-success status code")
errNewRequestFailure = errors.New("failed creating an HTTP request")
errDoRequestFailure = errors.New("http client failed while sending request")
errReadingBodyFailure = errors.New("failed while reading http response body")
errJSONUnmarshal = errors.New("failed unmarshaling JSON response payload")
errJSONMarshal = errors.New("failed marshaling item as JSON payload")
errFailedConfig = errors.New("ancla configuration error")
ErrFailedAuthentication = errors.New("failed to authentication with argus")
errNonSuccessResponse = errors.New("argus responded with a non-success status code")
errNewRequestFailure = errors.New("failed creating an HTTP request")
errDoRequestFailure = errors.New("http client failed while sending request")
errReadingBodyFailure = errors.New("failed while reading http response body")
errJSONUnmarshal = errors.New("failed unmarshaling JSON response payload")
errJSONMarshal = errors.New("failed marshaling item as JSON payload")
)

// BasicClientConfig contains config data for the client that will be used to
// make requests to the Argus client.
type BasicClientConfig struct {
// Address is the Argus URL (i.e. https://example-argus.io:8090)
Address string

// Bucket partition to be used by this client.
Bucket string

// HTTPClient refers to the client that will be used to send requests.
// (Optional) Defaults to http.DefaultClient.
HTTPClient *http.Client

// Auth provides the mechanism to add auth headers to outgoing requests.
// (Optional) If not provided, no auth headers are added.
Auth auth.Decorator

// PullInterval is how often listeners should get updates.
// (Optional). Defaults to 5 seconds.
PullInterval time.Duration
}

// BasicClient is the client used to make requests to Argus.
type BasicClient struct {
client *http.Client
auth auth.Decorator
storeBaseURL string
storeAPIPath string
bucket string
getLogger func(context.Context) *zap.Logger
}
Expand All @@ -81,38 +57,19 @@ type response struct {
}

const (
storeAPIPath = "/api/v1/store"
storeV1APIPath = "/api/v1/store"
errWrappedFmt = "%w: %s"
errStatusCodeFmt = "%w: received status %v"
errorHeaderKey = "errorHeader"
)

// Items is a slice of model.Item(s) .
type Items []model.Item

type BasicClientIn struct {
fx.In

Options Options `optional:"true" `
}

// ProvideBasicClient provides a new BasicClient.
func ProvideBasicClient(in BasicClientIn) (*BasicClient, error) {
client, err := NewBasicClient(in.Options)
if err != nil {
return nil, errors.Join(errFailedConfig, err)
}

return client, nil
}

// NewBasicClient creates a new BasicClient that can be used to
// make requests to Argus.
func NewBasicClient(opts Options) (*BasicClient, error) {
func NewBasicClient(opts ClientOptions) (*BasicClient, error) {
var client BasicClient

opts = append(defaultOptions, opts)
opts = append(opts, defaultValidateOptions)
opts = append(defaultClientOptions, opts)
opts = append(opts, defaultValidateClientOptions)

return &client, opts.apply(&client)
}
Expand Down
Loading

0 comments on commit e8c26d7

Please sign in to comment.