diff --git a/invitation.go b/invitation.go new file mode 100644 index 0000000..86afef5 --- /dev/null +++ b/invitation.go @@ -0,0 +1,22 @@ +package clerk + +import "encoding/json" + +type Invitation struct { + APIResource + Object string `json:"object"` + ID string `json:"id"` + EmailAddress string `json:"email_address"` + PublicMetadata json.RawMessage `json:"public_metadata"` + Revoked bool `json:"revoked,omitempty"` + Status string `json:"status"` + URL string `json:"url,omitempty"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` +} + +type InvitationList struct { + APIResource + Invitations []*Invitation `json:"data"` + TotalCount int64 `json:"total_count"` +} diff --git a/invitation/api.go b/invitation/api.go new file mode 100644 index 0000000..f0e74ec --- /dev/null +++ b/invitation/api.go @@ -0,0 +1,30 @@ +// Code generated by "gen"; DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. +package invitation + +import ( + "context" + + "github.com/clerk/clerk-sdk-go/v2" +) + +// List returns all invitations. +func List(ctx context.Context, params *ListParams) (*clerk.InvitationList, error) { + return getClient().List(ctx, params) +} + +// Create adds a new identifier to the allowlist. +func Create(ctx context.Context, params *CreateParams) (*clerk.Invitation, error) { + return getClient().Create(ctx, params) +} + +// Revoke revokes a pending invitation. +func Revoke(ctx context.Context, id string) (*clerk.Invitation, error) { + return getClient().Revoke(ctx, id) +} + +func getClient() *Client { + return &Client{ + Backend: clerk.GetBackend(), + } +} diff --git a/invitation/client.go b/invitation/client.go new file mode 100644 index 0000000..5c18b53 --- /dev/null +++ b/invitation/client.go @@ -0,0 +1,67 @@ +package invitation + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/clerk/clerk-sdk-go/v2" +) + +//go:generate go run ../cmd/gen/main.go + +const path = "/invitations" + +// Client is used to invoke the Invitations API. +type Client struct { + Backend clerk.Backend +} + +func NewClient(config *clerk.ClientConfig) *Client { + return &Client{ + Backend: clerk.NewBackend(&config.BackendConfig), + } +} + +type ListParams struct { + clerk.APIParams +} + +// List returns all invitations. +func (c *Client) List(ctx context.Context, params *ListParams) (*clerk.InvitationList, error) { + req := clerk.NewAPIRequest(http.MethodGet, fmt.Sprintf("%s?paginated=true", path)) + list := &clerk.InvitationList{} + err := c.Backend.Call(ctx, req, list) + return list, err +} + +type CreateParams struct { + clerk.APIParams + EmailAddress string `json:"email_address"` + PublicMetadata *json.RawMessage `json:"public_metadata,omitempty"` + RedirectURL *string `json:"redirect_url,omitempty"` + Notify *bool `json:"notify,omitempty"` + IgnoreExisting *bool `json:"ignore_existing,omitempty"` +} + +// Create adds a new identifier to the allowlist. +func (c *Client) Create(ctx context.Context, params *CreateParams) (*clerk.Invitation, error) { + req := clerk.NewAPIRequest(http.MethodPost, path) + req.SetParams(params) + invitation := &clerk.Invitation{} + err := c.Backend.Call(ctx, req, invitation) + return invitation, err +} + +// Revoke revokes a pending invitation. +func (c *Client) Revoke(ctx context.Context, id string) (*clerk.Invitation, error) { + path, err := clerk.JoinPath(path, id, "revoke") + if err != nil { + return nil, err + } + req := clerk.NewAPIRequest(http.MethodPost, path) + invitation := &clerk.Invitation{} + err = c.Backend.Call(ctx, req, invitation) + return invitation, err +} diff --git a/invitation/invitation_test.go b/invitation/invitation_test.go new file mode 100644 index 0000000..e1d1719 --- /dev/null +++ b/invitation/invitation_test.go @@ -0,0 +1,130 @@ +package invitation + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/clerk/clerk-sdk-go/v2" + "github.com/clerk/clerk-sdk-go/v2/clerktest" + "github.com/stretchr/testify/require" +) + +func TestInvitationList(t *testing.T) { + clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{ + HTTPClient: &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Out: json.RawMessage(`{ + "data": [{"id":"inv_123","email_address":"foo@bar.com"}], + "total_count": 1 +}`), + Path: "/v1/invitations", + Method: http.MethodGet, + }, + }, + })) + + list, err := List(context.Background(), &ListParams{}) + require.NoError(t, err) + require.Equal(t, int64(1), list.TotalCount) + require.Equal(t, 1, len(list.Invitations)) + require.Equal(t, "inv_123", list.Invitations[0].ID) + require.Equal(t, "foo@bar.com", list.Invitations[0].EmailAddress) +} + +func TestInvitationCreate(t *testing.T) { + emailAddress := "foo@bar.com" + id := "inv_123" + clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{ + HTTPClient: &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + In: json.RawMessage(fmt.Sprintf(`{"email_address":"%s"}`, emailAddress)), + Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","email_address":"%s"}`, id, emailAddress)), + Method: http.MethodPost, + Path: "/v1/invitations", + }, + }, + })) + + invitation, err := Create(context.Background(), &CreateParams{ + EmailAddress: emailAddress, + }) + require.NoError(t, err) + require.Equal(t, id, invitation.ID) + require.Equal(t, emailAddress, invitation.EmailAddress) +} + +func TestInvitationCreate_Error(t *testing.T) { + clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{ + HTTPClient: &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Status: http.StatusBadRequest, + Out: json.RawMessage(`{ + "errors":[{ + "code":"create-error-code" + }], + "clerk_trace_id":"create-trace-id" +}`), + }, + }, + })) + + _, err := Create(context.Background(), &CreateParams{}) + require.Error(t, err) + apiErr, ok := err.(*clerk.APIErrorResponse) + require.True(t, ok) + require.Equal(t, "create-trace-id", apiErr.TraceID) + require.Equal(t, 1, len(apiErr.Errors)) + require.Equal(t, "create-error-code", apiErr.Errors[0].Code) +} + +func TestInvitationRevoke(t *testing.T) { + id := "inv_123" + clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{ + HTTPClient: &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","revoked":true,"status":"revoked"}`, id)), + Method: http.MethodPost, + Path: "/v1/invitations/" + id + "/revoke", + }, + }, + })) + + invitation, err := Revoke(context.Background(), id) + require.NoError(t, err) + require.Equal(t, id, invitation.ID) + require.True(t, invitation.Revoked) + require.Equal(t, "revoked", invitation.Status) +} + +func TestInvitationRevoke_Error(t *testing.T) { + id := "inv_123" + clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{ + HTTPClient: &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Status: http.StatusBadRequest, + Out: json.RawMessage(`{ + "errors":[{ + "code":"revoke-error-code" + }], + "clerk_trace_id":"revoke-trace-id" +}`), + }, + }, + })) + + _, err := Revoke(context.Background(), id) + require.Error(t, err) + apiErr, ok := err.(*clerk.APIErrorResponse) + require.True(t, ok) + require.Equal(t, "revoke-trace-id", apiErr.TraceID) + require.Equal(t, 1, len(apiErr.Errors)) + require.Equal(t, "revoke-error-code", apiErr.Errors[0].Code) +}