-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add options pattern #276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,9 @@ | |
// Decorate decorates the given http request with authorization header(s). | ||
Decorate(ctx context.Context, req *http.Request) error | ||
} | ||
|
||
type DecoratorFunc func(context.Context, *http.Request) error | ||
|
||
func (f DecoratorFunc) Decorate(ctx context.Context, req *http.Request) error { return f(ctx, req) } | ||
|
||
var Nop = DecoratorFunc(func(context.Context, *http.Request) error { return nil }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made |
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) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,6 @@ import ( | |
"fmt" | ||
"io" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/xmidt-org/ancla/auth" | ||
"github.com/xmidt-org/ancla/model" | ||
|
@@ -25,53 +24,28 @@ const ( | |
) | ||
|
||
var ( | ||
ErrNilMeasures = errors.New("measures cannot be nil") | ||
ErrAddressEmpty = errors.New("argus address is required") | ||
ErrBucketEmpty = errors.New("bucket name is required") | ||
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 | ||
|
||
Comment on lines
-50
to
-53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. replaced by clientOptions |
||
// 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 | ||
} | ||
|
@@ -83,31 +57,32 @@ 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 | ||
|
||
// NewBasicClient creates a new BasicClient that can be used to | ||
// make requests to Argus. | ||
func NewBasicClient(config BasicClientConfig, | ||
getLogger func(context.Context) *zap.Logger) (*BasicClient, error) { | ||
err := validateBasicConfig(&config) | ||
if err != nil { | ||
return nil, err | ||
} | ||
func NewBasicClient(opts ...ClientOption) (*BasicClient, error) { | ||
var ( | ||
client BasicClient | ||
defaultClientOptions = ClientOptions{ | ||
// localhost defaults | ||
StoreBaseURL(""), | ||
StoreAPIPath(""), | ||
// Nop defaults | ||
HTTPClient(nil), | ||
GetClientLogger(nil), | ||
Auth(nil), | ||
} | ||
) | ||
|
||
return &BasicClient{ | ||
client: config.HTTPClient, | ||
auth: config.Auth, | ||
bucket: config.Bucket, | ||
storeBaseURL: config.Address + storeAPIPath, | ||
getLogger: getLogger, | ||
}, nil | ||
opts = append(defaultClientOptions, opts...) | ||
opts = append(opts, clientValidator()) | ||
|
||
return &client, ClientOptions(opts).apply(&client) | ||
} | ||
|
||
// GetItems fetches all items that belong to a given owner. | ||
|
@@ -251,19 +226,3 @@ func translateNonSuccessStatusCode(code int) error { | |
return errNonSuccessResponse | ||
} | ||
} | ||
|
||
func validateBasicConfig(config *BasicClientConfig) error { | ||
if config.Address == "" { | ||
return ErrAddressEmpty | ||
} | ||
|
||
if config.Bucket == "" { | ||
Comment on lines
-254
to
-260
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. replaced by clientOptions' validation |
||
return ErrBucketEmpty | ||
} | ||
|
||
if config.HTTPClient == nil { | ||
config.HTTPClient = http.DefaultClient | ||
} | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ancla will solely use the argus client until we discover a use case for swappable db clients