diff --git a/organization_domain.go b/organization_domain.go new file mode 100644 index 0000000..e2e0c23 --- /dev/null +++ b/organization_domain.go @@ -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"` +} diff --git a/organizationdomain/api.go b/organizationdomain/api.go new file mode 100644 index 0000000..2674f7e --- /dev/null +++ b/organizationdomain/api.go @@ -0,0 +1,35 @@ +// Code generated by "gen"; DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. +package organizationdomain + +import ( + "context" + + "github.com/clerk/clerk-sdk-go/v2" +) + +// Create adds a new domain to the organization. +func Create(ctx context.Context, organizationID string, params *CreateParams) (*clerk.OrganizationDomain, error) { + return getClient().Create(ctx, organizationID, params) +} + +// Update updates an organization domain. +func Update(ctx context.Context, params *UpdateParams) (*clerk.OrganizationDomain, error) { + return getClient().Update(ctx, params) +} + +// Delete removes a domain from an organization. +func Delete(ctx context.Context, params *DeleteParams) (*clerk.DeletedResource, error) { + return getClient().Delete(ctx, params) +} + +// List returns a list of organization domains. +func List(ctx context.Context, organizationID string, params *ListParams) (*clerk.OrganizationDomainList, error) { + return getClient().List(ctx, organizationID, params) +} + +func getClient() *Client { + return &Client{ + Backend: clerk.GetBackend(), + } +} diff --git a/organizationdomain/client.go b/organizationdomain/client.go new file mode 100644 index 0000000..5fdc5ba --- /dev/null +++ b/organizationdomain/client.go @@ -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 +} diff --git a/organizationdomain/client_test.go b/organizationdomain/client_test.go new file mode 100644 index 0000000..b5ba96b --- /dev/null +++ b/organizationdomain/client_test.go @@ -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) +}