Skip to content

Commit

Permalink
feat: Actor token API operations
Browse files Browse the repository at this point in the history
Added the types and operations for actor tokens. Create and Revoke are
supported.
  • Loading branch information
gkats committed Feb 1, 2024
1 parent 9f010f6 commit 096c7ce
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 82 deletions.
15 changes: 15 additions & 0 deletions actor_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package clerk

import "encoding/json"

type ActorToken struct {
APIResource
Object string `json:"object"`
ID string `json:"id"`
UserID string `json:"user_id"`
Actor json.RawMessage `json:"actor"`
Token string `json:"token,omitempty"`
Status string `json:"status"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
42 changes: 42 additions & 0 deletions actortoken/actor_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Package actortoken provides the Actor Tokens API.
package actortoken

import (
"context"
"encoding/json"
"net/http"
"net/url"

"github.com/clerk/clerk-sdk-go/v2"
)

const path = "/actor_tokens"

type CreateParams struct {
clerk.APIParams
UserID *string `json:"user_id,omitempty"`
Actor json.RawMessage `json:"actor,omitempty"`
ExpiresInSeconds *int64 `json:"expires_in_seconds,omitempty"`
SessionMaxDurationInSeconds *int64 `json:"session_max_duration_in_seconds,omitempty"`
}

// Create creates a new actor token.
func 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)
return token, err
}

// Revoke revokes a pending actor token.
func Revoke(ctx context.Context, id string) (*clerk.ActorToken, error) {
token := &clerk.ActorToken{}
path, err := url.JoinPath(path, id, "revoke")
if err != nil {
return token, err
}
req := clerk.NewAPIRequest(http.MethodPost, path)
err = clerk.GetBackend().Call(ctx, req, token)
return token, err
}
80 changes: 80 additions & 0 deletions actortoken/actor_token_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package actortoken

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 TestActorTokenCreate(t *testing.T) {
userID := "usr_123"
id := "act_123"
clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{
HTTPClient: &http.Client{
Transport: &clerktest.RoundTripper{
T: t,
In: json.RawMessage(fmt.Sprintf(`{"user_id":"%s"}`, userID)),
Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","user_id":"%s"}`, id, userID)),
Path: "/v1/actor_tokens",
Method: http.MethodPost,
},
},
}))

actorToken, err := Create(context.Background(), &CreateParams{
UserID: clerk.String(userID),
})
require.NoError(t, err)
require.Equal(t, id, actorToken.ID)
require.Equal(t, userID, actorToken.UserID)
}

func TestActorTokenCreate_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 TestActorTokenRevoke(t *testing.T) {
id := "act_456"
clerk.SetBackend(clerk.NewBackend(&clerk.BackendConfig{
HTTPClient: &http.Client{
Transport: &clerktest.RoundTripper{
T: t,
Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","status":"revoked"}`, id)),
Path: fmt.Sprintf("/v1/actor_tokens/%s/revoke", id),
Method: http.MethodPost,
},
},
}))

actorToken, err := Revoke(context.Background(), id)
require.NoError(t, err)
require.Equal(t, id, actorToken.ID)
require.Equal(t, "revoked", actorToken.Status)
}
55 changes: 55 additions & 0 deletions clerktest/clerktest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Package clerktest provides utilities for testing.
package clerktest

import (
"bytes"
"encoding/json"
"io"
"net/http"
"testing"

"github.com/stretchr/testify/require"
)

// RoundTripper can be used as a mock Transport for http.Clients.
// Set the RoundTripper's fields accordingly to determine the
// response or perform assertions on the http.Request properties.
type RoundTripper struct {
T *testing.T
// Status is the response Status code.
Status int
// Out is the response body.
Out json.RawMessage
// Set this field to assert on the request method.
Method string
// Set this field to assert that the request path matches.
Path string
// Set this field to assert that the request body matches.
In json.RawMessage
}

// RoundTrip returns an http.Response based on the RoundTripper's fields.
// It will also perform assertions on the http.Request.
func (rt *RoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
if rt.Status == 0 {
rt.Status = http.StatusOK
}
if rt.Path != "" {
require.Equal(rt.T, rt.Path, r.URL.Path)
}
if rt.Method != "" {
require.Equal(rt.T, rt.Method, r.Method)
}
if rt.In != nil {
body, err := io.ReadAll(r.Body)
if err != nil {
return nil, err
}
defer r.Body.Close()
require.JSONEq(rt.T, string(rt.In), string(body))
}
return &http.Response{
StatusCode: rt.Status,
Body: io.NopCloser(bytes.NewReader(rt.Out)),
}, nil
}
Loading

0 comments on commit 096c7ce

Please sign in to comment.