Skip to content

Commit

Permalink
ORGS-205: Update GO SDK to support organization domains crud (#326)
Browse files Browse the repository at this point in the history
* feat: implement organization domains CRUD

* linter

* Apply suggestions from code review

Co-authored-by: Giannis Katsanos <[email protected]>

* code review

* refactor: ensure the same pattern is being followed

---------

Co-authored-by: Giannis Katsanos <[email protected]>
  • Loading branch information
NicolasLopes7 and gkats authored Sep 18, 2024
1 parent 807c124 commit ff233cb
Show file tree
Hide file tree
Showing 4 changed files with 368 additions and 0 deletions.
29 changes: 29 additions & 0 deletions organization_domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package clerk

type OrganizationDomainVerification struct {
Status string `json:"status"`
Strategy string `json:"strategy"`
Attempts int64 `json:"attempts"`
ExpireAt *int64 `json:"expire_at"`
}

type OrganizationDomain struct {
APIResource
Object string `json:"object"`
ID string `json:"id"`
OrganizationID string `json:"organization_id"`
Name string `json:"name"`
EnrollmentMode string `json:"enrollment_mode"`
AffiliationEmailAddress *string `json:"affiliation_email_address"`
Verification *OrganizationDomainVerification `json:"verification"`
TotalPendingInvitations int64 `json:"total_pending_invitations"`
TotalPendingSuggestions int64 `json:"total_pending_suggestions"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}

type OrganizationDomainList struct {
APIResource
OrganizationDomains []*OrganizationDomain `json:"data"`
TotalCount int64 `json:"total_count"`
}
35 changes: 35 additions & 0 deletions organizationdomain/api.go

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

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

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

"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 Domains API.
type Client struct {
Backend clerk.Backend
}

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

type CreateParams struct {
clerk.APIParams
Name *string `json:"name,omitempty"`
EnrollmentMode *string `json:"enrollment_mode,omitempty"`
Verified *bool `json:"verified,omitempty"`
}

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

type UpdateParams struct {
clerk.APIParams
OrganizationID string `json:"-"`
DomainID string `json:"-"`
EnrollmentMode *string `json:"enrollment_mode,omitempty"`
Verified *bool `json:"verified,omitempty"`
}

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

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

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

type ListParams struct {
clerk.APIParams
clerk.ListParams
Verified *bool `json:"verified,omitempty"`
EnrollmentModes *[]string `json:"enrollment_mode,omitempty"`
}

// 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.Verified != nil {
q.Set("verified", strconv.FormatBool(*params.Verified))
}

if params.EnrollmentModes != nil && len(*params.EnrollmentModes) > 0 {
q["enrollment_mode"] = *params.EnrollmentModes
}
return q
}

// List returns a list of organization domains.
func (c *Client) List(ctx context.Context, organizationID string, params *ListParams) (*clerk.OrganizationDomainList, error) {
path, err := clerk.JoinPath(path, organizationID, "/domains")
if err != nil {
return nil, err
}
req := clerk.NewAPIRequest(http.MethodGet, path)
req.SetParams(params)
domains := &clerk.OrganizationDomainList{}
err = c.Backend.Call(ctx, req, domains)
return domains, err
}
184 changes: 184 additions & 0 deletions organizationdomain/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package organizationdomain

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"testing"

"github.com/clerk/clerk-sdk-go/v2"
"github.com/clerk/clerk-sdk-go/v2/clerktest"
"github.com/stretchr/testify/require"
)

func TestOrganizationDomainClientCreate(t *testing.T) {
t.Parallel()
id := "orgdm_123"
organizationID := "org_123"
domain := "mydomain.com"
verified := false
config := &clerk.ClientConfig{}
config.HTTPClient = &http.Client{
Transport: &clerktest.RoundTripper{
T: t,
In: json.RawMessage(fmt.Sprintf(`{"name": "%s", "enrollment_mode": "automatic_invitation", "verified": %s}`, domain, strconv.FormatBool(verified))),
Out: json.RawMessage(fmt.Sprintf(`{"enrollment_mode":"automatic_invitation","id":"%s","name":"%s","object":"organization_domain","organization_id":"%s","verification":{"status":"unverified"}}`,
id, domain, organizationID)),
Method: http.MethodPost,
Path: "/v1/organizations/" + organizationID + "/domains",
},
}
client := NewClient(config)
response, err := client.Create(context.Background(), organizationID, &CreateParams{
Name: clerk.String(domain),
EnrollmentMode: clerk.String("automatic_invitation"),
Verified: clerk.Bool(verified),
})
require.NoError(t, err)
require.Equal(t, id, response.ID)
require.Equal(t, domain, response.Name)
require.Equal(t, "automatic_invitation", response.EnrollmentMode)
require.Equal(t, "unverified", response.Verification.Status)
}

func TestOrganizationDomainClientCreate_Error(t *testing.T) {
t.Parallel()
config := &clerk.ClientConfig{}
config.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"
}`),
},
}
client := NewClient(config)
_, err := client.Create(context.Background(), "org_123", &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 TestOrganizationDomainClientUpdate(t *testing.T) {
t.Parallel()
id := "orgdm_123"
organizationID := "org_123"
verified := true
config := &clerk.ClientConfig{}
config.HTTPClient = &http.Client{
Transport: &clerktest.RoundTripper{
T: t,
In: json.RawMessage(fmt.Sprintf(`{"verified": %s}`, strconv.FormatBool(verified))),
Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","verification":{"status": "verified"}}`, id)),
Method: http.MethodPatch,
Path: "/v1/organizations/" + organizationID + "/domains/" + id,
},
}
client := NewClient(config)
domain, err := client.Update(context.Background(), &UpdateParams{
OrganizationID: organizationID,
DomainID: id,
Verified: clerk.Bool(verified),
})
require.NoError(t, err)
require.Equal(t, id, domain.ID)
require.Equal(t, "verified", domain.Verification.Status)
}

func TestOrganizationDomainClientUpdate_Error(t *testing.T) {
t.Parallel()
config := &clerk.ClientConfig{}
config.HTTPClient = &http.Client{
Transport: &clerktest.RoundTripper{
T: t,
Status: http.StatusBadRequest,
Out: json.RawMessage(`{
"errors":[{
"code":"update-error-code"
}],
"clerk_trace_id":"update-trace-id"
}`),
},
}
client := NewClient(config)
_, err := client.Update(context.Background(), &UpdateParams{})
require.Error(t, err)
apiErr, ok := err.(*clerk.APIErrorResponse)
require.True(t, ok)
require.Equal(t, "update-trace-id", apiErr.TraceID)
require.Equal(t, 1, len(apiErr.Errors))
require.Equal(t, "update-error-code", apiErr.Errors[0].Code)
}

func TestOrganizationDomainClientDelete(t *testing.T) {
t.Parallel()
id := "orgdm_123"
organizationID := "org_123"
config := &clerk.ClientConfig{}
config.HTTPClient = &http.Client{
Transport: &clerktest.RoundTripper{
T: t,
Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","object":"organization_domain"}`, id)),
Method: http.MethodDelete,
Path: "/v1/organizations/" + organizationID + "/domains/" + id,
},
}
client := NewClient(config)
deletedResource, err := client.Delete(context.Background(), &DeleteParams{
OrganizationID: organizationID,
DomainID: id,
})
require.NoError(t, err)
require.Equal(t, id, deletedResource.ID)
}

func TestOrganizationDomainClientList(t *testing.T) {
t.Parallel()
id := "orgdm_123"
domain := "mydomain.com"
organizationID := "org_123"
verified := true

config := &clerk.ClientConfig{}
config.HTTPClient = &http.Client{
Transport: &clerktest.RoundTripper{
T: t,
Out: json.RawMessage(fmt.Sprintf(`{
"data": [
{"enrollment_mode":"automatic_suggestion","id":"%s","name":"%s","object":"organization_domain","organization_id":"%s","verification":{"status":"unverified"}}
],
"total_count": 1
}`,
id, domain, organizationID)),
Method: http.MethodGet,
Path: "/v1/organizations/" + organizationID + "/domains",
Query: &url.Values{
"limit": []string{"1"},
"offset": []string{"2"},
"verified": []string{"true"},
"enrollment_mode": []string{"automatic_invitation"},
},
},
}
client := NewClient(config)
params := &ListParams{
Verified: &verified,
EnrollmentModes: &[]string{"automatic_invitation"},
}
params.Limit = clerk.Int64(1)
params.Offset = clerk.Int64(2)
list, err := client.List(context.Background(), organizationID, params)
require.NoError(t, err)
require.Equal(t, id, list.OrganizationDomains[0].ID)
require.Equal(t, organizationID, list.OrganizationDomains[0].OrganizationID)
}

0 comments on commit ff233cb

Please sign in to comment.