diff --git a/saml_connection.go b/saml_connection.go new file mode 100644 index 00000000..d51654fa --- /dev/null +++ b/saml_connection.go @@ -0,0 +1,39 @@ +package clerk + +type SAMLConnection struct { + APIResource + ID string `json:"id"` + Object string `json:"object"` + Name string `json:"name"` + Domain string `json:"domain"` + IdpEntityID *string `json:"idp_entity_id"` + IdpSsoURL *string `json:"idp_sso_url"` + IdpCertificate *string `json:"idp_certificate"` + IdpMetadataURL *string `json:"idp_metadata_url"` + IdpMetadata *string `json:"idp_metadata"` + AcsURL string `json:"acs_url"` + SPEntityID string `json:"sp_entity_id"` + SPMetadataURL string `json:"sp_metadata_url"` + AttributeMapping SAMLConnectionAttributeMapping `json:"attribute_mapping"` + Active bool `json:"active"` + Provider string `json:"provider"` + UserCount int64 `json:"user_count"` + SyncUserAttributes bool `json:"sync_user_attributes"` + AllowSubdomains bool `json:"allow_subdomains"` + AllowIdpInitiated bool `json:"allow_idp_initiated"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` +} + +type SAMLConnectionAttributeMapping struct { + UserID string `json:"user_id"` + EmailAddress string `json:"email_address"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` +} + +type SAMLConnectionList struct { + APIResource + SAMLConnections []*SAMLConnection `json:"data"` + TotalCount int64 `json:"total_count"` +} diff --git a/samlconnection/api.go b/samlconnection/api.go new file mode 100644 index 00000000..aa66f578 --- /dev/null +++ b/samlconnection/api.go @@ -0,0 +1,40 @@ +// Code generated by "gen"; DO NOT EDIT. +// This file is meant to be re-generated in place and/or deleted at any time. +package samlconnection + +import ( + "context" + + "github.com/clerk/clerk-sdk-go/v2" +) + +// Create creates a new SAML Connection. +func Create(ctx context.Context, params *CreateParams) (*clerk.SAMLConnection, error) { + return getClient().Create(ctx, params) +} + +// Get returns details about a SAML Connection. +func Get(ctx context.Context, id string) (*clerk.SAMLConnection, error) { + return getClient().Get(ctx, id) +} + +// Update updates the SAML Connection specified by id. +func Update(ctx context.Context, id string, params *UpdateParams) (*clerk.SAMLConnection, error) { + return getClient().Update(ctx, id, params) +} + +// Delete deletes a SAML Connection. +func Delete(ctx context.Context, id string) (*clerk.DeletedResource, error) { + return getClient().Delete(ctx, id) +} + +// List returns a list of SAML Connections. +func List(ctx context.Context, params *ListParams) (*clerk.SAMLConnectionList, error) { + return getClient().List(ctx, params) +} + +func getClient() *Client { + return &Client{ + Backend: clerk.GetBackend(), + } +} diff --git a/samlconnection/client.go b/samlconnection/client.go new file mode 100644 index 00000000..72964061 --- /dev/null +++ b/samlconnection/client.go @@ -0,0 +1,119 @@ +// Package samlconnection provides the SAML Connections API. +package samlconnection + +import ( + "context" + "net/http" + + "github.com/clerk/clerk-sdk-go/v2" +) + +//go:generate go run ../cmd/gen/main.go + +const path = "/saml_connections" + +// Client is used to invoke the SAML Connections API. +type Client struct { + Backend clerk.Backend +} + +func NewClient(config *clerk.ClientConfig) *Client { + return &Client{ + Backend: clerk.NewBackend(&config.BackendConfig), + } +} + +type AttributeMappingParams struct { + UserID string `json:"user_id"` + EmailAddress string `json:"email_address"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` +} + +type CreateParams struct { + clerk.APIParams + Name *string `json:"name,omitempty"` + Domain *string `json:"domain,omitempty"` + Provider *string `json:"provider,omitempty"` + IdpEntityID *string `json:"idp_entity_id,omitempty"` + IdpSsoURL *string `json:"idp_sso_url,omitempty"` + IdpCertificate *string `json:"idp_certificate,omitempty"` + IdpMetadataURL *string `json:"idp_metadata_url,omitempty"` + IdpMetadata *string `json:"idp_metadata,omitempty"` + AttributeMapping *AttributeMappingParams `json:"attribute_mapping,omitempty"` +} + +// Create creates a new SAML Connection. +func (c *Client) Create(ctx context.Context, params *CreateParams) (*clerk.SAMLConnection, error) { + req := clerk.NewAPIRequest(http.MethodPost, path) + req.SetParams(params) + connection := &clerk.SAMLConnection{} + err := c.Backend.Call(ctx, req, connection) + return connection, err +} + +// Get returns details about a SAML Connection. +func (c *Client) Get(ctx context.Context, id string) (*clerk.SAMLConnection, error) { + path, err := clerk.JoinPath(path, id) + if err != nil { + return nil, err + } + req := clerk.NewAPIRequest(http.MethodGet, path) + connection := &clerk.SAMLConnection{} + err = c.Backend.Call(ctx, req, connection) + return connection, err +} + +type UpdateParams struct { + clerk.APIParams + Name *string `json:"name,omitempty"` + Domain *string `json:"domain,omitempty"` + IdpEntityID *string `json:"idp_entity_id,omitempty"` + IdpSsoURL *string `json:"idp_sso_url,omitempty"` + IdpCertificate *string `json:"idp_certificate,omitempty"` + IdpMetadataURL *string `json:"idp_metadata_url,omitempty"` + IdpMetadata *string `json:"idp_metadata,omitempty"` + AttributeMapping *AttributeMappingParams `json:"attribute_mapping,omitempty"` + Active *bool `json:"active,omitempty"` + SyncUserAttributes *bool `json:"sync_user_attributes,omitempty"` + AllowSubdomains *bool `json:"allow_subdomains,omitempty"` + AllowIdpInitiated *bool `json:"allow_idp_initiated,omitempty"` +} + +// Update updates the SAML Connection specified by id. +func (c *Client) Update(ctx context.Context, id string, params *UpdateParams) (*clerk.SAMLConnection, error) { + path, err := clerk.JoinPath(path, id) + if err != nil { + return nil, err + } + req := clerk.NewAPIRequest(http.MethodPatch, path) + req.SetParams(params) + connection := &clerk.SAMLConnection{} + err = c.Backend.Call(ctx, req, connection) + return connection, err +} + +// Delete deletes a SAML Connection. +func (c *Client) Delete(ctx context.Context, id string) (*clerk.DeletedResource, error) { + path, err := clerk.JoinPath(path, id) + if err != nil { + return nil, err + } + req := clerk.NewAPIRequest(http.MethodDelete, path) + template := &clerk.DeletedResource{} + err = c.Backend.Call(ctx, req, template) + return template, err +} + +type ListParams struct { + clerk.APIParams +} + +// List returns a list of SAML Connections. +func (c *Client) List(ctx context.Context, params *ListParams) (*clerk.SAMLConnectionList, error) { + req := clerk.NewAPIRequest(http.MethodGet, path) + req.SetParams(params) + list := &clerk.SAMLConnectionList{} + err := c.Backend.Call(ctx, req, list) + return list, err +} diff --git a/samlconnection/client_test.go b/samlconnection/client_test.go new file mode 100644 index 00000000..6d9492ad --- /dev/null +++ b/samlconnection/client_test.go @@ -0,0 +1,188 @@ +package samlconnection + +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 TestSAMLConnectionClientCreate(t *testing.T) { + t.Parallel() + id := "samlc__123" + name := "the-name" + domain := "example.com" + provider := "saml_custom" + config := &clerk.ClientConfig{} + config.HTTPClient = &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + In: json.RawMessage(fmt.Sprintf(`{"name":"%s","domain":"%s","provider":"%s"}`, name, domain, provider)), + Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","name":"%s","domain":"%s","provider":"%s"}`, id, name, domain, provider)), + Method: http.MethodPost, + Path: "/v1/saml_connections", + }, + } + client := NewClient(config) + samlConnection, err := client.Create(context.Background(), &CreateParams{ + Name: clerk.String(name), + Domain: clerk.String(domain), + Provider: clerk.String(provider), + }) + require.NoError(t, err) + require.Equal(t, id, samlConnection.ID) + require.Equal(t, name, samlConnection.Name) + require.Equal(t, domain, samlConnection.Domain) + require.Equal(t, provider, samlConnection.Provider) +} + +func TestSAMLConnectionClientCreate_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(), &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 TestSAMLConnectionClientGet(t *testing.T) { + t.Parallel() + id := "samlc__123" + name := "the-name" + domain := "example.com" + provider := "saml_custom" + config := &clerk.ClientConfig{} + config.HTTPClient = &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","name":"%s","domain":"%s","provider":"%s"}`, id, name, domain, provider)), Method: http.MethodGet, + Path: "/v1/saml_connections/" + id, + }, + } + client := NewClient(config) + samlConnection, err := client.Get(context.Background(), id) + require.NoError(t, err) + require.Equal(t, id, samlConnection.ID) + require.Equal(t, name, samlConnection.Name) + require.Equal(t, domain, samlConnection.Domain) + require.Equal(t, provider, samlConnection.Provider) +} + +func TestSAMLConnectionClientUpdate(t *testing.T) { + t.Parallel() + id := "samlc__123" + name := "the-name" + domain := "example.com" + provider := "saml_custom" + config := &clerk.ClientConfig{} + config.HTTPClient = &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + In: json.RawMessage(fmt.Sprintf(`{"name":"%s"}`, name)), + Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","name":"%s","domain":"%s","provider":"%s"}`, id, name, domain, provider)), + Method: http.MethodPatch, + Path: "/v1/saml_connections/" + id, + }, + } + client := NewClient(config) + samlConnection, err := client.Update(context.Background(), id, &UpdateParams{ + Name: clerk.String(name), + }) + require.NoError(t, err) + require.Equal(t, id, samlConnection.ID) + require.Equal(t, name, samlConnection.Name) +} + +func TestSAMLConnectionClientUpdate_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(), "jtmpl_123", &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 TestSAMLConnectionClientDelete(t *testing.T) { + t.Parallel() + id := "samlc__123" + config := &clerk.ClientConfig{} + config.HTTPClient = &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Out: json.RawMessage(fmt.Sprintf(`{"id":"%s","deleted":true}`, id)), + Method: http.MethodDelete, + Path: "/v1/saml_connections/" + id, + }, + } + client := NewClient(config) + samlConnection, err := client.Delete(context.Background(), id) + require.NoError(t, err) + require.Equal(t, id, samlConnection.ID) + require.True(t, samlConnection.Deleted) +} + +func TestSAMLConnectionClientList(t *testing.T) { + t.Parallel() + id := "samlc__123" + name := "the-name" + domain := "example.com" + provider := "saml_custom" + config := &clerk.ClientConfig{} + config.HTTPClient = &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Out: json.RawMessage(fmt.Sprintf(`{ + "data": [{"id":"%s","name":"%s","domain":"%s","provider":"%s"}], + "total_count": 1 +}`, id, name, domain, provider)), + Method: http.MethodGet, + Path: "/v1/saml_connections", + }, + } + client := NewClient(config) + list, err := client.List(context.Background(), &ListParams{}) + require.NoError(t, err) + require.Equal(t, int64(1), list.TotalCount) + require.Equal(t, 1, len(list.SAMLConnections)) + require.Equal(t, id, list.SAMLConnections[0].ID) + require.Equal(t, name, list.SAMLConnections[0].Name) + require.Equal(t, domain, list.SAMLConnections[0].Domain) + require.Equal(t, provider, list.SAMLConnections[0].Provider) +}