Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Organization Memberships API #231

Merged
merged 1 commit into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clerk.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func setRequestQuery(req *http.Request, params Params) {
paramsQuery := params.ToQuery()
for k, values := range paramsQuery {
for _, v := range values {
q.Set(k, v)
q.Add(k, v)
Copy link
Member Author

@gkats gkats Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@georgepsarakis I think you've voiced a concern about "array" parameters in the query string and how using Set won't work with them.

Your instinct was right. We need to add values and not overwrite them in order to support arrays.

}
}
req.URL.RawQuery = q.Encode()
Expand Down
25 changes: 9 additions & 16 deletions clerk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,15 @@ type testResourceList struct {
type testResourceListParams struct {
APIParams
ListParams
Name string
Overriden string
Name string
Appended string
}

// We need to implement the Params interface.
func (params testResourceListParams) ToQuery() url.Values {
q := url.Values{}
q := params.ListParams.ToQuery()
q.Set("name", params.Name)
q.Set("overriden", params.Overriden)
listQ := params.ListParams.ToQuery()
for k, values := range listQ {
for _, v := range values {
q.Add(k, v)
}
}
q.Set("appended", params.Appended)
return q
}

Expand Down Expand Up @@ -234,7 +228,6 @@ func TestBackendCall_SuccessfulResponse_PostRequest(t *testing.T) {
func TestBackendCall_SuccessfulResponse_GetRequest(t *testing.T) {
ctx := context.Background()
name := "the-name"
overriden := "true"
limit := 1
rawJSON := `{"data": [{"id":"res_123","object":"resource"}], "total_count": 1}`

Expand All @@ -249,8 +242,8 @@ func TestBackendCall_SuccessfulResponse_GetRequest(t *testing.T) {
assert.False(t, ok)
// Existing query parameters are preserved
assert.Equal(t, "still-here", q.Get("existing"))
// Existing query parameters can be overriden
assert.Equal(t, overriden, q.Get("overriden"))
// Existing query parameters will be appended, not overriden
assert.Equal(t, []string{"false", "true"}, q["appended"])

_, err := w.Write([]byte(rawJSON))
require.NoError(t, err)
Expand All @@ -266,13 +259,13 @@ func TestBackendCall_SuccessfulResponse_GetRequest(t *testing.T) {
// Simulate usage for an API operation on a testResourceList.
// We need to initialize a request and use the Backend to send it.
resource := &testResourceList{}
req := NewAPIRequest(http.MethodGet, "/resources?existing=still-here&overriden=false")
req := NewAPIRequest(http.MethodGet, "/resources?existing=still-here&appended=false")
req.SetParams(&testResourceListParams{
ListParams: ListParams{
Limit: Int64(int64(limit)),
},
Name: name,
Overriden: overriden,
Name: name,
Appended: "true",
})
err := GetBackend().Call(ctx, req, resource)
require.NoError(t, err)
Expand Down
9 changes: 4 additions & 5 deletions organization/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,10 @@ func (c *Client) DeleteLogo(ctx context.Context, id string) (*clerk.Organization
type ListParams struct {
clerk.APIParams
clerk.ListParams
IncludeMembersCount *bool `json:"include_members_count,omitempty"`
OrderBy *string `json:"order_by,omitempty"`
Query *string `json:"query,omitempty"`
// TODO do we need a pointer here? Probably not.
UserIDs []string `json:"user_id,omitempty"`
IncludeMembersCount *bool `json:"include_members_count,omitempty"`
OrderBy *string `json:"order_by,omitempty"`
Query *string `json:"query,omitempty"`
UserIDs []string `json:"user_id,omitempty"`
}

// ToQuery returns query string values from the params.
Expand Down
2 changes: 2 additions & 0 deletions organization/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,15 @@ func TestOrganizationClientList(t *testing.T) {
"offset": []string{"2"},
"order_by": []string{"-created_at"},
"query": []string{"Acme"},
"user_id": []string{"user_123", "user_456"},
},
},
}
client := NewClient(config)
params := &ListParams{
OrderBy: clerk.String("-created_at"),
Query: clerk.String("Acme"),
UserIDs: []string{"user_123", "user_456"},
}
params.Limit = clerk.Int64(1)
params.Offset = clerk.Int64(2)
Expand Down
32 changes: 32 additions & 0 deletions organization_membership.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package clerk

import "encoding/json"

type OrganizationMembership struct {
APIResource
Object string `json:"object"`
ID string `json:"id"`
Organization *Organization `json:"organization"`
Permissions []string `json:"permissions"`
PublicMetadata json.RawMessage `json:"public_metadata"`
PrivateMetadata json.RawMessage `json:"private_metadata"`
Role string `json:"role"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
PublicUserData *OrganizationMembershipPublicUserData `json:"public_user_data,omitempty"`
}

type OrganizationMembershipList struct {
APIResource
OrganizationMemberships []*OrganizationMembership `json:"data"`
TotalCount int64 `json:"total_count"`
}

type OrganizationMembershipPublicUserData struct {
UserID string `json:"user_id"`
FirstName *string `json:"first_name"`
LastName *string `json:"last_name"`
ImageURL *string `json:"image_url"`
HasImage bool `json:"has_image"`
Identifier string `json:"identifier"`
}
36 changes: 36 additions & 0 deletions organizationmembership/api.go

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

145 changes: 145 additions & 0 deletions organizationmembership/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Package organizationmembership provides the Organization Memberships API.
package organizationmembership

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

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

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

const path = "/organizations"

// Client is used to invoke the Organization Memberships 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"`
Role *string `json:"role,omitempty"`
OrganizationID string `json:"-"`
}

// Create adds a new member to the organization.
func (c *Client) Create(ctx context.Context, params *CreateParams) (*clerk.OrganizationMembership, error) {
path, err := clerk.JoinPath(path, params.OrganizationID, "/memberships")
if err != nil {
return nil, err
}
req := clerk.NewAPIRequest(http.MethodPost, path)
req.SetParams(params)
membership := &clerk.OrganizationMembership{}
err = c.Backend.Call(ctx, req, membership)
return membership, err
}

type UpdateParams struct {
clerk.APIParams
Role *string `json:"role,omitempty"`
OrganizationID string `json:"-"`
UserID string `json:"-"`
}

// Update updates an organization membership.
func (c *Client) Update(ctx context.Context, params *UpdateParams) (*clerk.OrganizationMembership, error) {
path, err := clerk.JoinPath(path, params.OrganizationID, "/memberships", params.UserID)
if err != nil {
return nil, err
}
req := clerk.NewAPIRequest(http.MethodPatch, path)
req.SetParams(params)
membership := &clerk.OrganizationMembership{}
err = c.Backend.Call(ctx, req, membership)
return membership, err
}

type DeleteParams struct {
clerk.APIParams
OrganizationID string `json:"-"`
UserID string `json:"-"`
}

// Delete removes a member from an organization.
func (c *Client) Delete(ctx context.Context, params *DeleteParams) (*clerk.OrganizationMembership, error) {
path, err := clerk.JoinPath(path, params.OrganizationID, "/memberships", params.UserID)
if err != nil {
return nil, err
}
req := clerk.NewAPIRequest(http.MethodDelete, path)
membership := &clerk.OrganizationMembership{}
err = c.Backend.Call(ctx, req, membership)
return membership, err
}

type ListParams struct {
clerk.APIParams
clerk.ListParams
OrderBy *string `json:"order_by,omitempty"`
Query *string `json:"query,omitempty"`
Roles []string `json:"role,omitempty"`
UserIDs []string `json:"user_id,omitempty"`
EmailAddresses []string `json:"email_address,omitempty"`
PhoneNumbers []string `json:"phone_number,omitempty"`
Usernames []string `json:"username,omitempty"`
Web3Wallets []string `json:"web3_wallet,omitempty"`
OrganizationID string `json:"-"`
}

// ToQuery returns the parameters as url.Values so they can be used
// in a URL query string.
func (params *ListParams) ToQuery() url.Values {
q := params.ListParams.ToQuery()
if params.OrderBy != nil {
q.Set("order_by", *params.OrderBy)
}
if params.Query != nil {
q.Set("query", *params.Query)
}
if params.Roles != nil {
q["role"] = params.Roles
}
if params.UserIDs != nil {
q["user_id"] = params.UserIDs
}
if params.EmailAddresses != nil {
q["email_address"] = params.EmailAddresses
}
if params.PhoneNumbers != nil {
q["phone_number"] = params.PhoneNumbers
}
if params.Usernames != nil {
q["username"] = params.Usernames
}
if params.Web3Wallets != nil {
q["web3_wallet"] = params.Web3Wallets
}
return q
}

// List returns a list of organization memberships.
func (c *Client) List(ctx context.Context, params *ListParams) (*clerk.OrganizationMembershipList, error) {
path, err := clerk.JoinPath(path, params.OrganizationID, "/memberships")
if err != nil {
return nil, err
}
req := clerk.NewAPIRequest(http.MethodGet, path)
req.SetParams(params)
list := &clerk.OrganizationMembershipList{}
err = c.Backend.Call(ctx, req, list)
return list, err
}
Loading
Loading