Skip to content

Commit

Permalink
Clients for API operations (#219)
Browse files Browse the repository at this point in the history
* feat: Generator for API functions

Added a quick and dirty generator that takes a <package>/client.go file
and turns each of the <package>.Client methods into functions.
This allows us to automatically generate package-level functions for
each API operation. We write the Client methods and then call go
generate to handle creating API operations on the package.
As an example, let's say we have a domain package and a domain.Client,
which supports a Create method. Usage would then be
  domain.NewClient().Create()
When we run go generate for all packages, we'll also get a
  domain.Create()
function which calls the Client method under the hood.

* feat: Clients for API operations

Up until now, our SDK offered only one way to perform API operations.
That is package level functions like actortoken.Create(). These
package-level functions would use a global Backend to send requests to
the Clerk API.
This design works well for most cases, but is a bit inflexible. The
existence of a global Backend means that there's no way to configure
individual API operations (override secret key, url, HTTP client).
We now provide an alternative API, which uses clients with a dedicated
Backend for each individual API.
  • Loading branch information
gkats authored Feb 7, 2024
1 parent 43ce4a8 commit e15c401
Show file tree
Hide file tree
Showing 9 changed files with 383 additions and 20 deletions.
26 changes: 26 additions & 0 deletions actortoken/api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 21 additions & 4 deletions actortoken/actor_token.go → actortoken/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,25 @@ import (
"github.com/clerk/clerk-sdk-go/v2"
)

//go:generate go run ../cmd/gen/main.go

const path = "/actor_tokens"

// Client is used to invoke the Actor Tokens API.
type Client struct {
Backend clerk.Backend
}

type ClientConfig struct {
clerk.BackendConfig
}

func NewClient(config *ClientConfig) *Client {
return &Client{
Backend: clerk.NewBackend(&config.BackendConfig),
}
}

type CreateParams struct {
clerk.APIParams
UserID *string `json:"user_id,omitempty"`
Expand All @@ -20,22 +37,22 @@ type CreateParams struct {
}

// Create creates a new actor token.
func Create(ctx context.Context, params *CreateParams) (*clerk.ActorToken, error) {
func (c *Client) Create(ctx context.Context, params *CreateParams) (*clerk.ActorToken, error) {
req := clerk.NewAPIRequest(http.MethodPost, path)
req.SetParams(params)
token := &clerk.ActorToken{}
err := clerk.GetBackend().Call(ctx, req, token)
err := c.Backend.Call(ctx, req, token)
return token, err
}

// Revoke revokes a pending actor token.
func Revoke(ctx context.Context, id string) (*clerk.ActorToken, error) {
func (c *Client) Revoke(ctx context.Context, id string) (*clerk.ActorToken, error) {
token := &clerk.ActorToken{}
path, err := clerk.JoinPath(path, id, "revoke")
if err != nil {
return token, err
}
req := clerk.NewAPIRequest(http.MethodPost, path)
err = clerk.GetBackend().Call(ctx, req, token)
err = c.Backend.Call(ctx, req, token)
return token, err
}
31 changes: 31 additions & 0 deletions allowlistidentifier/api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,49 @@ import (
"github.com/clerk/clerk-sdk-go/v2"
)

//go:generate go run ../cmd/gen/main.go

const path = "/allowlist_identifiers"

// Client is used to invoke the Allowlist Identifiers API.
type Client struct {
Backend clerk.Backend
}

type ClientConfig struct {
clerk.BackendConfig
}

func NewClient(config *ClientConfig) *Client {
return &Client{
Backend: clerk.NewBackend(&config.BackendConfig),
}
}

type CreateParams struct {
clerk.APIParams
Identifier *string `json:"identifier,omitempty"`
Notify *bool `json:"notify,omitempty"`
}

// Create adds a new identifier to the allowlist.
func Create(ctx context.Context, params *CreateParams) (*clerk.AllowlistIdentifier, error) {
func (c *Client) Create(ctx context.Context, params *CreateParams) (*clerk.AllowlistIdentifier, error) {
req := clerk.NewAPIRequest(http.MethodPost, path)
req.SetParams(params)
identifier := &clerk.AllowlistIdentifier{}
err := clerk.GetBackend().Call(ctx, req, identifier)
err := c.Backend.Call(ctx, req, identifier)
return identifier, err
}

// Delete removes an identifier from the allowlist.
func Delete(ctx context.Context, id string) (*clerk.DeletedResource, error) {
func (c *Client) Delete(ctx context.Context, id string) (*clerk.DeletedResource, error) {
path, err := url.JoinPath(path, id)
if err != nil {
return nil, err
}
req := clerk.NewAPIRequest(http.MethodDelete, path)
identifier := &clerk.DeletedResource{}
err = clerk.GetBackend().Call(ctx, req, identifier)
err = c.Backend.Call(ctx, req, identifier)
return identifier, err
}

Expand All @@ -44,9 +61,9 @@ type ListParams struct {
}

// List returns all the identifiers in the allowlist.
func List(ctx context.Context, params *ListParams) (*clerk.AllowlistIdentifierList, error) {
func (c *Client) List(ctx context.Context, params *ListParams) (*clerk.AllowlistIdentifierList, error) {
req := clerk.NewAPIRequest(http.MethodGet, fmt.Sprintf("%s?paginated=true", path))
list := &clerk.AllowlistIdentifierList{}
err := clerk.GetBackend().Call(ctx, req, list)
err := c.Backend.Call(ctx, req, list)
return list, err
}
9 changes: 8 additions & 1 deletion clerk.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ type BackendConfig struct {
// URL is the base URL to use for API endpoints.
// If it's not set, the default value for the Backend will be used.
URL *string
// Key TODO
Key *string
}

// NewBackend returns a default backend implementation with the
Expand All @@ -159,9 +161,13 @@ func NewBackend(config *BackendConfig) Backend {
if config.URL == nil {
config.URL = String(APIURL)
}
if config.Key == nil {
config.Key = String(secretKey)
}
return &defaultBackend{
HTTPClient: config.HTTPClient,
URL: *config.URL,
Key: *config.Key,
}
}

Expand Down Expand Up @@ -196,6 +202,7 @@ func SetBackend(b Backend) {
type defaultBackend struct {
HTTPClient *http.Client
URL string
Key string
}

// Call sends requests to the Clerk API and handles the responses.
Expand All @@ -217,7 +224,7 @@ func (b *defaultBackend) newRequest(ctx context.Context, apiReq *APIRequest) (*h
if err != nil {
return nil, err
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", secretKey))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", b.Key))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("User-Agent", fmt.Sprintf("Clerk/%s SDK-Go/%s", clerkAPIVersion, sdkVersion))
req.Header.Add("X-Clerk-SDK", fmt.Sprintf("go/%s", sdkVersion))
Expand Down
6 changes: 6 additions & 0 deletions clerk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,28 @@ func TestNewAPIResponse(t *testing.T) {
}

func TestNewBackend(t *testing.T) {
defaultSecretKey := "sk_test_123"
SetKey(defaultSecretKey)
withDefaults, ok := NewBackend(&BackendConfig{}).(*defaultBackend)
require.True(t, ok)
require.NotNil(t, withDefaults.HTTPClient)
assert.Equal(t, defaultHTTPTimeout, withDefaults.HTTPClient.Timeout)
assert.Equal(t, APIURL, withDefaults.URL)
assert.Equal(t, defaultSecretKey, withDefaults.Key)

u := "https://some.other.url"
httpClient := &http.Client{}
secretKey := defaultSecretKey + "diff"
config := &BackendConfig{
URL: &u,
HTTPClient: httpClient,
Key: &secretKey,
}
withOverrides, ok := NewBackend(config).(*defaultBackend)
require.True(t, ok)
assert.Equal(t, u, withOverrides.URL)
assert.Equal(t, httpClient, withOverrides.HTTPClient)
assert.Equal(t, secretKey, withOverrides.Key)
}

func TestGetBackend_DataRace(t *testing.T) {
Expand Down
Loading

0 comments on commit e15c401

Please sign in to comment.