Skip to content

Commit

Permalink
Drop the With option prefixes
Browse files Browse the repository at this point in the history
  • Loading branch information
chriso committed Jun 7, 2024
1 parent 245f09a commit 54486bc
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 104 deletions.
53 changes: 22 additions & 31 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"net/http"
"os"
"slices"
"strings"

"buf.build/gen/go/stealthrocket/dispatch-proto/connectrpc/go/dispatch/sdk/v1/sdkv1connect"
sdkv1 "buf.build/gen/go/stealthrocket/dispatch-proto/protocolbuffers/go/dispatch/sdk/v1"
Expand All @@ -33,15 +31,15 @@ func NewClient(opts ...ClientOption) (*Client, error) {
env: os.Environ(),
}
for _, opt := range opts {
opt(c)
opt.configureClient(c)
}

if c.apiKey == "" {
c.apiKey = getenv(c.env, "DISPATCH_API_KEY")
c.apiKeyFromEnv = true
}
if c.apiKey == "" {
return nil, fmt.Errorf("Dispatch API key has not been set. Use WithAPIKey(..), or set the DISPATCH_API_KEY environment variable")
return nil, fmt.Errorf("Dispatch API key has not been set. Use APIKey(..), or set the DISPATCH_API_KEY environment variable")
}

if c.apiUrl == "" {
Expand Down Expand Up @@ -75,35 +73,35 @@ func NewClient(opts ...ClientOption) (*Client, error) {
}

// ClientOption configures a Client.
type ClientOption func(*Client)
type ClientOption interface {
configureClient(d *Client)
}

type clientOptionFunc func(d *Client)

func (fn clientOptionFunc) configureClient(d *Client) {
fn(d)
}

// WithAPIKey sets the Dispatch API key to use for authentication when
// APIKey sets the Dispatch API key to use for authentication when
// dispatching function calls through a Client.
//
// It defaults to the value of the DISPATCH_API_KEY environment variable.
func WithAPIKey(apiKey string) ClientOption {
return func(c *Client) { c.apiKey = apiKey }
func APIKey(apiKey string) ClientOption {
return clientOptionFunc(func(c *Client) { c.apiKey = apiKey })
}

// WithAPIUrl sets the URL of the Dispatch API.
// APIUrl sets the URL of the Dispatch API.
//
// It defaults to the value of the DISPATCH_API_URL environment variable,
// or DefaultApiUrl if DISPATCH_API_URL is unset.
func WithAPIUrl(apiUrl string) ClientOption {
return func(c *Client) { c.apiUrl = apiUrl }
func APIUrl(apiUrl string) ClientOption {
return clientOptionFunc(func(c *Client) { c.apiUrl = apiUrl })
}

// DefaultApiUrl is the default Dispatch API URL.
const DefaultApiUrl = "https://api.dispatch.run"

// WithClientEnv sets the environment variables that a Client parses
// its default configuration from.
//
// It defaults to os.Environ().
func WithClientEnv(env ...string) ClientOption {
return func(c *Client) { c.env = slices.Clone(env) }
}

// Dispatch dispatches a function call.
func (c *Client) Dispatch(ctx context.Context, call Call) (ID, error) {
batch := c.Batch()
Expand All @@ -115,6 +113,10 @@ func (c *Client) Dispatch(ctx context.Context, call Call) (ID, error) {
return ids[0], nil
}

func (c *Client) configureDispatch(d *Dispatch) {
d.client = c
}

// Batch creates a Batch.
func (c *Client) Batch() Batch {
return Batch{client: c}
Expand Down Expand Up @@ -149,7 +151,7 @@ func (b *Batch) Dispatch(ctx context.Context) ([]ID, error) {
if b.client.apiKeyFromEnv {
return nil, fmt.Errorf("invalid DISPATCH_API_KEY: %s", redactAPIKey(b.client.apiKey))
}
return nil, fmt.Errorf("invalid Dispatch API key provided with WithAPIKey(): %s", redactAPIKey(b.client.apiKey))
return nil, fmt.Errorf("invalid Dispatch API key provided with APIKey(..): %s", redactAPIKey(b.client.apiKey))
}
return nil, err
}
Expand All @@ -160,17 +162,6 @@ func (b *Batch) Dispatch(ctx context.Context) ([]ID, error) {
return ids, nil
}

func getenv(env []string, name string) string {
var value string
for _, s := range env {
n, v, ok := strings.Cut(s, "=")
if ok && n == name {
value = v
}
}
return value
}

func redactAPIKey(s string) string {
if len(s) <= 3 {
// Don't redact the string if it's this short. It's not a valid API
Expand Down
10 changes: 5 additions & 5 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestClient(t *testing.T) {
recorder := &dispatchtest.CallRecorder{}
server := dispatchtest.NewDispatchServer(recorder)

client, err := dispatch.NewClient(dispatch.WithAPIKey("foobar"), dispatch.WithAPIUrl(server.URL))
client, err := dispatch.NewClient(dispatch.APIKey("foobar"), dispatch.APIUrl(server.URL))
if err != nil {
t.Fatal(err)
}
Expand All @@ -34,7 +34,7 @@ func TestClientEnvConfig(t *testing.T) {
recorder := &dispatchtest.CallRecorder{}
server := dispatchtest.NewDispatchServer(recorder)

client, err := dispatch.NewClient(dispatch.WithClientEnv(
client, err := dispatch.NewClient(dispatch.Env(
"DISPATCH_API_KEY=foobar",
"DISPATCH_API_URL="+server.URL,
))
Expand All @@ -59,7 +59,7 @@ func TestClientBatch(t *testing.T) {
recorder := &dispatchtest.CallRecorder{}
server := dispatchtest.NewDispatchServer(recorder)

client, err := dispatch.NewClient(dispatch.WithAPIKey("foobar"), dispatch.WithAPIUrl(server.URL))
client, err := dispatch.NewClient(dispatch.APIKey("foobar"), dispatch.APIUrl(server.URL))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -96,10 +96,10 @@ func TestClientBatch(t *testing.T) {
}

func TestClientNoAPIKey(t *testing.T) {
_, err := dispatch.NewClient(dispatch.WithClientEnv( /* i.e. no env vars */ ))
_, err := dispatch.NewClient(dispatch.Env( /* i.e. no env vars */ ))
if err == nil {
t.Fatalf("expected an error")
} else if err.Error() != "Dispatch API key has not been set. Use WithAPIKey(..), or set the DISPATCH_API_KEY environment variable" {
} else if err.Error() != "Dispatch API key has not been set. Use APIKey(..), or set the DISPATCH_API_KEY environment variable" {
t.Errorf("unexpected error: %v", err)
}
}
72 changes: 27 additions & 45 deletions dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"net/http"
"net/url"
"os"
"slices"
"strings"
"sync"

Expand Down Expand Up @@ -44,30 +43,24 @@ func New(opts ...DispatchOption) (*Dispatch, error) {
functions: map[string]Function{},
}
for _, opt := range opts {
opt(d)
opt.configureDispatch(d)
}

// Prepare the endpoint URL.
var endpointUrlFromEnv string
var endpointUrlFromEnv bool
if d.endpointUrl == "" {
d.endpointUrl = getenv(d.env, "DISPATCH_ENDPOINT_URL")
endpointUrlFromEnv = "DISPATCH_ENDPOINT_URL"
endpointUrlFromEnv = true
}
if d.endpointUrl == "" {
if endpointAddr := getenv(d.env, "DISPATCH_ENDPOINT_ADDR"); endpointAddr != "" {
d.endpointUrl = fmt.Sprintf("http://%s", endpointAddr)
endpointUrlFromEnv = "DISPATCH_ENDPOINT_ADDR"
}
}
if d.endpointUrl == "" {
return nil, fmt.Errorf("Dispatch endpoint URL has not been set. Use WithEndpointUrl(..), or set the DISPATCH_ENDPOINT_URL environment variable")
return nil, fmt.Errorf("Dispatch endpoint URL has not been set. Use EndpointUrl(..), or set the DISPATCH_ENDPOINT_URL environment variable")
}
_, err := url.Parse(d.endpointUrl)
if err != nil {
if endpointUrlFromEnv != "" {
return nil, fmt.Errorf("invalid %s: %v", endpointUrlFromEnv, d.endpointUrl)
if endpointUrlFromEnv {
return nil, fmt.Errorf("invalid DISPATCH_ENDPOINT_URL: %v", d.endpointUrl)
}
return nil, fmt.Errorf("invalid endpoint URL provided via WithEndpointUrl: %v", d.endpointUrl)
return nil, fmt.Errorf("invalid endpoint URL provided via EndpointUrl(..): %v", d.endpointUrl)
}

// Prepare the address to serve on.
Expand All @@ -92,7 +85,7 @@ func New(opts ...DispatchOption) (*Dispatch, error) {
if verificationKeyFromEnv {
return nil, fmt.Errorf("invalid DISPATCH_VERIFICATION_KEY: %v", d.verificationKey)
}
return nil, fmt.Errorf("invalid verification key provided via WithVerificationKey: %v", d.verificationKey)
return nil, fmt.Errorf("invalid verification key provided via VerificationKey(..): %v", d.verificationKey)
}
}

Expand All @@ -118,7 +111,7 @@ func New(opts ...DispatchOption) (*Dispatch, error) {
// Optionally attach a client.
if d.client == nil {
var err error
d.client, err = NewClient(append(d.clientOpts, WithClientEnv(d.env...))...)
d.client, err = NewClient(append(d.clientOpts, Env(d.env...))...)
if err != nil {
slog.Debug("failed to setup client for the Dispatch endpoint", "error", err)
d.clientErr = err
Expand All @@ -129,17 +122,25 @@ func New(opts ...DispatchOption) (*Dispatch, error) {
}

// DispatchOption configures a Dispatch endpoint.
type DispatchOption func(d *Dispatch)
type DispatchOption interface {
configureDispatch(d *Dispatch)
}

type dispatchOptionFunc func(d *Dispatch)

func (fn dispatchOptionFunc) configureDispatch(d *Dispatch) {
fn(d)
}

// WithEndpointUrl sets the URL of the Dispatch endpoint.
// EndpointUrl sets the URL of the Dispatch endpoint.
//
// It defaults to the value of the DISPATCH_ENDPOINT_URL environment
// variable.
func WithEndpointUrl(endpointUrl string) DispatchOption {
return func(d *Dispatch) { d.endpointUrl = endpointUrl }
func EndpointUrl(endpointUrl string) DispatchOption {
return dispatchOptionFunc(func(d *Dispatch) { d.endpointUrl = endpointUrl })
}

// WithVerificationKey sets the verification key to use when verifying
// VerificationKey sets the verification key to use when verifying
// Dispatch request signatures.
//
// The key should be a PEM or base64-encoded ed25519 public key.
Expand All @@ -149,30 +150,11 @@ func WithEndpointUrl(endpointUrl string) DispatchOption {
//
// If a verification key is not provided, request signatures will
// not be validated.
func WithVerificationKey(verificationKey string) DispatchOption {
return func(d *Dispatch) { d.verificationKey = verificationKey }
}

// WithEnv sets the environment variables that a Dispatch endpoint
// parses its default configuration from.
//
// It defaults to os.Environ().
func WithEnv(env ...string) DispatchOption {
return func(d *Dispatch) { d.env = slices.Clone(env) }
}

// WithClient binds a Client to a Dispatch endpoint.
//
// Binding a Client allows functions calls to be directly dispatched from
// functions registered with the endpoint, via function.Dispatch(...).
//
// The Dispatch endpoint will attempt to create a Client automatically,
// using configuration from the environment.
func WithClient(client *Client) DispatchOption {
return func(d *Dispatch) { d.client = client }
func VerificationKey(verificationKey string) DispatchOption {
return dispatchOptionFunc(func(d *Dispatch) { d.verificationKey = verificationKey })
}

// WithServeAddress sets the address that the Dispatch endpoint
// ServeAddress sets the address that the Dispatch endpoint
// is served on (see Dispatch.Serve).
//
// Note that this is not the same as the endpoint URL, which is the
Expand All @@ -181,8 +163,8 @@ func WithClient(client *Client) DispatchOption {
// It defaults to the value of the DISPATCH_ENDPOINT_ADDR environment
// variable, which is automatically set by the Dispatch CLI. If this
// is unset, it defaults to 127.0.0.1:8000.
func WithServeAddress(addr string) DispatchOption {
return func(d *Dispatch) { d.serveAddr = addr }
func ServeAddress(addr string) DispatchOption {
return dispatchOptionFunc(func(d *Dispatch) { d.serveAddr = addr })
}

// Register registers a function.
Expand Down
Loading

0 comments on commit 54486bc

Please sign in to comment.