Skip to content

Commit

Permalink
Factor out a dispatchclient package
Browse files Browse the repository at this point in the history
  • Loading branch information
chriso committed Jun 21, 2024
1 parent aeddcee commit 2c29d74
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 170 deletions.
64 changes: 39 additions & 25 deletions dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import (
sdkv1 "buf.build/gen/go/stealthrocket/dispatch-proto/protocolbuffers/go/dispatch/sdk/v1"
"connectrpc.com/connect"
"connectrpc.com/validate"
"github.com/dispatchrun/dispatch-go/dispatchclient"
"github.com/dispatchrun/dispatch-go/dispatchproto"
"github.com/dispatchrun/dispatch-go/internal/auth"
"github.com/dispatchrun/dispatch-go/internal/env"
)

// Dispatch is a Dispatch endpoint.
Expand All @@ -27,9 +29,9 @@ type Dispatch struct {
verificationKey string
serveAddr string
env []string
opts []DispatchOption
opts []Option

client *Client
client *dispatchclient.Client
clientErr error

path string
Expand All @@ -39,19 +41,19 @@ type Dispatch struct {
}

// New creates a Dispatch endpoint.
func New(opts ...DispatchOption) (*Dispatch, error) {
func New(opts ...Option) (*Dispatch, error) {
d := &Dispatch{
env: os.Environ(),
opts: opts,
}
for _, opt := range opts {
opt.configureDispatch(d)
opt(d)
}

// Prepare the endpoint URL.
var endpointUrlFromEnv bool
if d.endpointUrl == "" {
d.endpointUrl = getenv(d.env, "DISPATCH_ENDPOINT_URL")
d.endpointUrl = env.Get(d.env, "DISPATCH_ENDPOINT_URL")
endpointUrlFromEnv = true
}
if d.endpointUrl == "" {
Expand All @@ -67,7 +69,7 @@ func New(opts ...DispatchOption) (*Dispatch, error) {

// Prepare the address to serve on.
if d.serveAddr == "" {
d.serveAddr = getenv(d.env, "DISPATCH_ENDPOINT_ADDR")
d.serveAddr = env.Get(d.env, "DISPATCH_ENDPOINT_ADDR")
if d.serveAddr == "" {
d.serveAddr = "127.0.0.1:8000"
}
Expand All @@ -76,7 +78,7 @@ func New(opts ...DispatchOption) (*Dispatch, error) {
// Prepare the verification key.
var verificationKeyFromEnv bool
if d.verificationKey == "" {
d.verificationKey = getenv(d.env, "DISPATCH_VERIFICATION_KEY")
d.verificationKey = env.Get(d.env, "DISPATCH_VERIFICATION_KEY")
verificationKeyFromEnv = true
}
var verificationKey ed25519.PublicKey
Expand Down Expand Up @@ -111,29 +113,21 @@ func New(opts ...DispatchOption) (*Dispatch, error) {

// Optionally attach a client.
if d.client == nil {
d.client, d.clientErr = NewClient(Env(d.env...))
d.client, d.clientErr = dispatchclient.New(dispatchclient.Env(d.env...))
}

return d, nil
}

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

type dispatchOptionFunc func(d *Dispatch)

func (fn dispatchOptionFunc) configureDispatch(d *Dispatch) {
fn(d)
}
// Option configures a Dispatch endpoint.
type Option func(d *Dispatch)

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

// VerificationKey sets the verification key to use when verifying
Expand All @@ -146,8 +140,8 @@ func EndpointUrl(endpointUrl string) DispatchOption {
//
// If a verification key is not provided, request signatures will
// not be validated.
func VerificationKey(verificationKey string) DispatchOption {
return dispatchOptionFunc(func(d *Dispatch) { d.verificationKey = verificationKey })
func VerificationKey(verificationKey string) Option {
return func(d *Dispatch) { d.verificationKey = verificationKey }
}

// ServeAddress sets the address that the Dispatch endpoint
Expand All @@ -159,8 +153,28 @@ func VerificationKey(verificationKey string) 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 ServeAddress(addr string) DispatchOption {
return dispatchOptionFunc(func(d *Dispatch) { d.serveAddr = addr })
func ServeAddress(addr string) Option {
return func(d *Dispatch) { d.serveAddr = addr }
}

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

// Client sets the client to use when dispatching calls
// from functions registered on the endpoint.
//
// By default the Dispatch endpoint will attempt to construct
// a dispatchclient.Client instance using the DISPATCH_API_KEY
// and optional DISPATCH_API_URL environment variables. If more
// control is required over client configuration, the custom
// client instance can be registered here and used instead.
func Client(client *dispatchclient.Client) Option {
return func(d *Dispatch) { d.client = client }
}

// Register registers a function.
Expand All @@ -185,7 +199,7 @@ func (d *Dispatch) Handler() (string, http.Handler) {
}

// Client returns the Client attached to this endpoint.
func (d *Dispatch) Client() (*Client, error) {
func (d *Dispatch) Client() (*dispatchclient.Client, error) {
return d.client, d.clientErr
}

Expand Down
9 changes: 5 additions & 4 deletions dispatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"connectrpc.com/connect"
"github.com/dispatchrun/dispatch-go"
"github.com/dispatchrun/dispatch-go/dispatchclient"
"github.com/dispatchrun/dispatch-go/dispatchproto"
"github.com/dispatchrun/dispatch-go/dispatchtest"
)
Expand Down Expand Up @@ -76,12 +77,12 @@ func TestDispatchCall(t *testing.T) {
recorder := &dispatchtest.CallRecorder{}
server := dispatchtest.NewServer(recorder)

client, err := dispatch.NewClient(dispatch.APIKey("foobar"), dispatch.APIUrl(server.URL))
client, err := dispatchclient.New(dispatchclient.APIKey("foobar"), dispatchclient.APIUrl(server.URL))
if err != nil {
t.Fatal(err)
}

endpoint, err := dispatch.New(dispatch.EndpointUrl("http://example.com"), client)
endpoint, err := dispatch.New(dispatch.EndpointUrl("http://example.com"), dispatch.Client(client))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -144,12 +145,12 @@ func TestDispatchCallsBatch(t *testing.T) {

server := dispatchtest.NewServer(&recorder)

client, err := dispatch.NewClient(dispatch.APIKey("foobar"), dispatch.APIUrl(server.URL))
client, err := dispatchclient.New(dispatchclient.APIKey("foobar"), dispatchclient.APIUrl(server.URL))
if err != nil {
t.Fatal(err)
}

endpoint, err := dispatch.New(dispatch.EndpointUrl("http://example.com"), client)
endpoint, err := dispatch.New(dispatch.EndpointUrl("http://example.com"), dispatch.Client(client))
if err != nil {
t.Fatal(err)
}
Expand Down
47 changes: 21 additions & 26 deletions client.go → dispatchclient/client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
//go:build !durable

package dispatch
package dispatchclient

import (
"context"
Expand All @@ -14,6 +12,7 @@ import (
"connectrpc.com/connect"
"connectrpc.com/validate"
"github.com/dispatchrun/dispatch-go/dispatchproto"
"github.com/dispatchrun/dispatch-go/internal/env"
)

const defaultApiUrl = "https://api.dispatch.run"
Expand All @@ -27,31 +26,31 @@ type Client struct {
apiUrl string
env []string
httpClient *http.Client
opts []ClientOption
opts []Option

client sdkv1connect.DispatchServiceClient
}

// NewClient creates a Client.
func NewClient(opts ...ClientOption) (*Client, error) {
// New creates a Client.
func New(opts ...Option) (*Client, error) {
c := &Client{
env: os.Environ(),
opts: opts,
}
for _, opt := range opts {
opt.configureClient(c)
opt(c)
}

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

if c.apiUrl == "" {
c.apiUrl = getenv(c.env, "DISPATCH_API_URL")
c.apiUrl = env.Get(c.env, "DISPATCH_API_URL")
}
if c.apiUrl == "" {
c.apiUrl = defaultApiUrl
Expand Down Expand Up @@ -81,31 +80,31 @@ func NewClient(opts ...ClientOption) (*Client, error) {
}

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

type clientOptionFunc func(d *Client)

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

// 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 APIKey(apiKey string) ClientOption {
return clientOptionFunc(func(c *Client) { c.apiKey = apiKey })
func APIKey(apiKey string) Option {
return func(c *Client) { c.apiKey = apiKey }
}

// APIUrl sets the URL of the Dispatch API.
//
// It defaults to the value of the DISPATCH_API_URL environment variable,
// or the default API URL (https://api.dispatch.run) if DISPATCH_API_URL
// is unset.
func APIUrl(apiUrl string) ClientOption {
return clientOptionFunc(func(c *Client) { c.apiUrl = apiUrl })
func APIUrl(apiUrl string) Option {
return func(c *Client) { c.apiUrl = apiUrl }
}

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

// Dispatch dispatches a function call.
Expand All @@ -119,10 +118,6 @@ func (c *Client) Dispatch(ctx context.Context, call dispatchproto.Call) (dispatc
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
12 changes: 6 additions & 6 deletions client_test.go → dispatchclient/client_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package dispatch_test
package dispatchclient_test

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

"github.com/dispatchrun/dispatch-go"
"github.com/dispatchrun/dispatch-go/dispatchclient"
"github.com/dispatchrun/dispatch-go/dispatchproto"
"github.com/dispatchrun/dispatch-go/dispatchtest"
)
Expand All @@ -14,7 +14,7 @@ func TestClient(t *testing.T) {
recorder := &dispatchtest.CallRecorder{}
server := dispatchtest.NewServer(recorder)

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

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

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

func TestClientNoAPIKey(t *testing.T) {
_, err := dispatch.NewClient(dispatch.Env( /* i.e. no env vars */ ))
_, err := dispatchclient.New(dispatchclient.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 APIKey(..), or set the DISPATCH_API_KEY environment variable" {
Expand Down
24 changes: 24 additions & 0 deletions dispatchclient/serde.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dispatchclient

import "github.com/dispatchrun/coroutine/types"

func init() {
types.Register(clientSerializer, clientDeserializer)
}

func clientSerializer(s *types.Serializer, c *Client) error {
types.SerializeT(s, c.opts)
return nil
}

func clientDeserializer(d *types.Deserializer, c *Client) error {
var opts []Option
types.DeserializeTo(d, &opts)

client, err := New(opts...)
if err != nil {
return err
}
*c = *client
return nil
}
Loading

0 comments on commit 2c29d74

Please sign in to comment.