diff --git a/pkg/accessreview/drivers/clerk.go b/pkg/accessreview/drivers/clerk.go deleted file mode 100644 index cee71a58b..000000000 --- a/pkg/accessreview/drivers/clerk.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package drivers - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "go.probo.inc/probo/pkg/coredata" -) - -const ( - clerkUsersEndpoint = "https://api.clerk.com/v1/users" - clerkUsersPageSize = 100 -) - -type ClerkDriver struct { - httpClient *http.Client -} - -var _ Driver = (*ClerkDriver)(nil) - -type clerkUser struct { - ID string `json:"id"` - PrimaryEmailAddressID *string `json:"primary_email_address_id"` - Username *string `json:"username"` - FirstName *string `json:"first_name"` - LastName *string `json:"last_name"` - PasswordEnabled bool `json:"password_enabled"` - TwoFactorEnabled bool `json:"two_factor_enabled"` - TOTPEnabled bool `json:"totp_enabled"` - BackupCodeEnabled bool `json:"backup_code_enabled"` - Banned bool `json:"banned"` - Locked bool `json:"locked"` - Deprovisioned bool `json:"deprovisioned"` - LastSignInAt *int64 `json:"last_sign_in_at"` - CreatedAt int64 `json:"created_at"` - EmailAddresses []struct { - ID string `json:"id"` - EmailAddress string `json:"email_address"` - } `json:"email_addresses"` -} - -func NewClerkDriver(httpClient *http.Client) *ClerkDriver { - return &ClerkDriver{ - httpClient: &http.Client{ - Transport: &retryRoundTripper{ - next: httpClient.Transport, - maxRetries: 3, - }, - }, - } -} - -func (d *ClerkDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) { - var ( - records []AccountRecord - offset = 0 - ) - - for range maxPaginationPages { - users, err := d.fetchUsersPage(ctx, offset) - if err != nil { - return nil, err - } - - for _, u := range users { - email := clerkPrimaryEmail(u) - if email == "" { - continue - } - - record := AccountRecord{ - Email: email, - FullName: clerkFullName(u, email), - Active: new(!u.Banned && !u.Locked && !u.Deprovisioned), - IsAdmin: false, - MFAStatus: clerkMFAStatus(u), - AuthMethod: clerkAuthMethod(u), - AccountType: coredata.AccessReviewEntryAccountTypeUser, - ExternalID: u.ID, - } - - if createdAt := clerkUnixMillisToTime(u.CreatedAt); createdAt != nil { - record.CreatedAt = createdAt - } - - if u.LastSignInAt != nil { - record.LastLogin = clerkUnixMillisToTime(*u.LastSignInAt) - } - - records = append(records, record) - } - - offset += len(users) - - if len(users) < clerkUsersPageSize { - return records, nil - } - } - - return nil, fmt.Errorf("cannot list all clerk users: %w", ErrPaginationLimitReached) -} - -func (d *ClerkDriver) fetchUsersPage(ctx context.Context, offset int) ([]clerkUser, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, clerkUsersEndpoint, nil) - if err != nil { - return nil, fmt.Errorf("cannot create clerk users request: %w", err) - } - - q := req.URL.Query() - q.Set("limit", strconv.Itoa(clerkUsersPageSize)) - q.Set("offset", strconv.Itoa(offset)) - req.URL.RawQuery = q.Encode() - - req.Header.Set("Accept", "application/json") - - httpResp, err := d.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("cannot execute clerk users request: %w", err) - } - - defer func() { - _ = httpResp.Body.Close() - }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return nil, fmt.Errorf("cannot fetch clerk users: unexpected status %d", httpResp.StatusCode) - } - - // GET /v1/users returns a bare JSON array of user objects; the total - // count is exposed separately via /v1/users/count. Decode directly - // into a slice. - var users []clerkUser - if err := json.NewDecoder(httpResp.Body).Decode(&users); err != nil { - return nil, fmt.Errorf("cannot decode clerk users response: %w", err) - } - - return users, nil -} - -func clerkPrimaryEmail(u clerkUser) string { - if u.PrimaryEmailAddressID != nil && *u.PrimaryEmailAddressID != "" { - for _, email := range u.EmailAddresses { - if email.ID == *u.PrimaryEmailAddressID && email.EmailAddress != "" { - return email.EmailAddress - } - } - } - - for _, email := range u.EmailAddresses { - if email.EmailAddress != "" { - return email.EmailAddress - } - } - - return "" -} - -func clerkFullName(u clerkUser, fallback string) string { - firstName := "" - lastName := "" - username := "" - - if u.FirstName != nil { - firstName = *u.FirstName - } - - if u.LastName != nil { - lastName = *u.LastName - } - - if u.Username != nil { - username = *u.Username - } - - fullName := strings.TrimSpace(firstName + " " + lastName) - if fullName != "" { - return fullName - } - - if username != "" { - return username - } - - return fallback -} - -func clerkMFAStatus(u clerkUser) coredata.MFAStatus { - if u.TwoFactorEnabled || u.TOTPEnabled || u.BackupCodeEnabled { - return coredata.MFAStatusEnabled - } - - return coredata.MFAStatusDisabled -} - -func clerkAuthMethod(u clerkUser) coredata.AccessReviewEntryAuthMethod { - if u.PasswordEnabled { - return coredata.AccessReviewEntryAuthMethodPassword - } - - return coredata.AccessReviewEntryAuthMethodUnknown -} - -func clerkUnixMillisToTime(unixMillis int64) *time.Time { - if unixMillis <= 0 { - return nil - } - - t := time.UnixMilli(unixMillis).UTC() - - return &t -} diff --git a/pkg/accessreview/drivers/clerk_test.go b/pkg/accessreview/drivers/clerk_test.go deleted file mode 100644 index ad13ff4a5..000000000 --- a/pkg/accessreview/drivers/clerk_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package drivers - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.probo.inc/probo/pkg/coredata" -) - -func TestClerkDriver(t *testing.T) { - t.Parallel() - - rec := newRecorder(t, "testdata/clerk", "CLERK_SECRET_KEY") - client := newVCRClient(rec, bearerAuth(os.Getenv("CLERK_SECRET_KEY"))) - - driver := NewClerkDriver(client) - records, err := driver.ListAccounts(context.Background()) - require.NoError(t, err) - require.Len(t, records, 3) - - // Clerk returns users newest-first (default order_by=-created_at). - first := records[0] - assert.Equal(t, "user_3EfkCEWmtIsoMD3rRxIpDsBOPzv", first.ExternalID) - assert.Equal(t, "c@example.com", first.Email) - assert.Equal(t, "c c", first.FullName) - assert.Equal(t, coredata.AccessReviewEntryAccountTypeUser, first.AccountType) - require.NotNil(t, first.Active) - assert.True(t, *first.Active) - assert.Equal(t, coredata.MFAStatusDisabled, first.MFAStatus) - assert.Equal(t, coredata.AccessReviewEntryAuthMethodPassword, first.AuthMethod) - assert.NotNil(t, first.CreatedAt) - assert.Nil(t, first.LastLogin) - - second := records[1] - assert.Equal(t, "b@example.com", second.Email) - assert.Equal(t, "b b", second.FullName) - require.NotNil(t, second.Active) - assert.True(t, *second.Active) - - // a@example.com is locked, so it must be reported inactive. - third := records[2] - assert.Equal(t, "a@example.com", third.Email) - assert.Equal(t, "a a", third.FullName) - require.NotNil(t, third.Active) - assert.False(t, *third.Active) - assert.Equal(t, coredata.AccessReviewEntryAuthMethodPassword, third.AuthMethod) -} - -func TestClerkPrimaryEmail(t *testing.T) { - t.Parallel() - - user := clerkUser{ - PrimaryEmailAddressID: new("eml_primary"), - EmailAddresses: []struct { - ID string `json:"id"` - EmailAddress string `json:"email_address"` - }{ - {ID: "eml_secondary", EmailAddress: "secondary@example.com"}, - {ID: "eml_primary", EmailAddress: "primary@example.com"}, - }, - } - - assert.Equal(t, "primary@example.com", clerkPrimaryEmail(user)) -} diff --git a/pkg/accessreview/drivers/testdata/clerk.yaml b/pkg/accessreview/drivers/testdata/clerk.yaml deleted file mode 100644 index 1fdf431aa..000000000 --- a/pkg/accessreview/drivers/testdata/clerk.yaml +++ /dev/null @@ -1,43 +0,0 @@ ---- -version: 2 -interactions: - - id: 0 - request: - proto: HTTP/1.1 - proto_major: 1 - proto_minor: 1 - content_length: 0 - host: api.clerk.com - form: - limit: - - "100" - offset: - - "0" - headers: - Accept: - - application/json - url: https://api.clerk.com/v1/users?limit=100&offset=0 - method: GET - response: - proto: HTTP/2.0 - proto_major: 2 - proto_minor: 0 - content_length: -1 - uncompressed: true - body: '[{"id":"user_3EfkCEWmtIsoMD3rRxIpDsBOPzv","object":"user","username":null,"first_name":"c","last_name":"c","locale":null,"image_url":"https://img.clerk.com/anonymized","has_image":false,"primary_email_address_id":"idn_3EfkCCq17KQsZzWLhtKnhAlguva","primary_phone_number_id":null,"primary_web3_wallet_id":null,"password_enabled":true,"two_factor_enabled":false,"totp_enabled":false,"backup_code_enabled":false,"email_addresses":[{"id":"idn_3EfkCCq17KQsZzWLhtKnhAlguva","object":"email_address","email_address":"c@example.com","reserved":false,"verification":{"object":"verification_admin","status":"verified","strategy":"admin","attempts":null,"expire_at":null},"linked_to":[],"matches_sso_connection":false,"created_at":1780576881106,"updated_at":1780576881106}],"phone_numbers":[],"web3_wallets":[],"passkeys":[],"external_accounts":[],"saml_accounts":[],"enterprise_accounts":[],"password_last_updated_at":1780576881104,"public_metadata":{},"private_metadata":{},"unsafe_metadata":{},"external_id":null,"last_sign_in_at":null,"banned":false,"locked":false,"lockout_expires_in_seconds":null,"verification_attempts_remaining":100,"created_at":1780576881104,"updated_at":1780576881109,"delete_self_enabled":true,"bypass_client_trust":false,"create_organization_enabled":true,"last_active_at":null,"mfa_enabled_at":null,"mfa_disabled_at":null,"legal_accepted_at":null,"requires_password_reset":false,"deprovisioned":false,"profile_image_url":"https://www.gravatar.com/avatar?d=mp"},{"id":"user_3EfkAUnJSm40lEsbkAuF9xUs5p2","object":"user","username":null,"first_name":"b","last_name":"b","locale":null,"image_url":"https://img.clerk.com/anonymized","has_image":false,"primary_email_address_id":"idn_3EfkAT1dfG5X3qrNnhntuaNC85w","primary_phone_number_id":null,"primary_web3_wallet_id":null,"password_enabled":true,"two_factor_enabled":false,"totp_enabled":false,"backup_code_enabled":false,"email_addresses":[{"id":"idn_3EfkAT1dfG5X3qrNnhntuaNC85w","object":"email_address","email_address":"b@example.com","reserved":false,"verification":{"object":"verification_admin","status":"verified","strategy":"admin","attempts":null,"expire_at":null},"linked_to":[],"matches_sso_connection":false,"created_at":1780576867751,"updated_at":1780576867751}],"phone_numbers":[],"web3_wallets":[],"passkeys":[],"external_accounts":[],"saml_accounts":[],"enterprise_accounts":[],"password_last_updated_at":1780576867746,"public_metadata":{},"private_metadata":{},"unsafe_metadata":{},"external_id":null,"last_sign_in_at":null,"banned":false,"locked":false,"lockout_expires_in_seconds":null,"verification_attempts_remaining":100,"created_at":1780576867746,"updated_at":1780576960608,"delete_self_enabled":true,"bypass_client_trust":false,"create_organization_enabled":true,"last_active_at":null,"mfa_enabled_at":null,"mfa_disabled_at":null,"legal_accepted_at":null,"requires_password_reset":true,"deprovisioned":false,"profile_image_url":"https://www.gravatar.com/avatar?d=mp"},{"id":"user_3Efk88E4dBmhk95VtBSXcOJ9vCx","object":"user","username":null,"first_name":"a","last_name":"a","locale":null,"image_url":"https://img.clerk.com/anonymized","has_image":false,"primary_email_address_id":"idn_3Efk87Z8o0h9gP86FnA8lUC2Alz","primary_phone_number_id":null,"primary_web3_wallet_id":null,"password_enabled":true,"two_factor_enabled":false,"totp_enabled":false,"backup_code_enabled":false,"email_addresses":[{"id":"idn_3Efk87Z8o0h9gP86FnA8lUC2Alz","object":"email_address","email_address":"a@example.com","reserved":false,"verification":{"object":"verification_admin","status":"verified","strategy":"admin","attempts":null,"expire_at":null},"linked_to":[],"matches_sso_connection":false,"created_at":1780576848230,"updated_at":1780576848230}],"phone_numbers":[],"web3_wallets":[],"passkeys":[],"external_accounts":[],"saml_accounts":[],"enterprise_accounts":[],"password_last_updated_at":1780576848227,"public_metadata":{},"private_metadata":{},"unsafe_metadata":{},"external_id":null,"last_sign_in_at":null,"banned":false,"locked":true,"lockout_expires_in_seconds":3554,"verification_attempts_remaining":100,"created_at":1780576848227,"updated_at":1780576947260,"delete_self_enabled":true,"bypass_client_trust":false,"create_organization_enabled":true,"last_active_at":null,"mfa_enabled_at":null,"mfa_disabled_at":null,"legal_accepted_at":null,"requires_password_reset":false,"deprovisioned":false,"profile_image_url":"https://www.gravatar.com/avatar?d=mp"}]' - headers: - Cf-Cache-Status: - - DYNAMIC - Clerk-Api-Version: - - "2025-11-10" - Content-Type: - - application/json - Date: - - Thu, 04 Jun 2026 12:43:12 GMT - Server: - - cloudflare - X-Cfworker: - - "1" - status: 200 OK - code: 200 - duration: 221.280708ms diff --git a/pkg/connector/provider/builtin.go b/pkg/connector/provider/builtin.go index 204de7eb0..edaf267fb 100644 --- a/pkg/connector/provider/builtin.go +++ b/pkg/connector/provider/builtin.go @@ -35,7 +35,6 @@ func NewBuiltinRegistry() *Registry { bitbucketRegistration(), brevoRegistration(), brexRegistration(), - clerkRegistration(), clickhouseRegistration(), clickupRegistration(), cloudflareRegistration(), diff --git a/pkg/connector/provider/clerk.go b/pkg/connector/provider/clerk.go deleted file mode 100644 index 4e0ccf1c3..000000000 --- a/pkg/connector/provider/clerk.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package provider - -import ( - "context" - "net/http" - - "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/accessreview/drivers" - "go.probo.inc/probo/pkg/coredata" -) - -func clerkRegistration() *Registration { - return &Registration{ - Provider: coredata.ConnectorProviderClerk, - DisplayName: "Clerk", - SupportsAPIKey: true, - // Clerk's Backend API authenticates with a server-side secret key - // (sk_...) presented as Authorization: Bearer, the default - // APIKeyConnection scheme. There is no third-party OAuth2 flow for - // account-listing: Clerk's OAuth is an end-user IdP (scoped consent - // to a single user's profile), not a partner grant over the Backend - // API. The secret key is bound to one Clerk instance, so there is - // nothing to pick (Pattern 3): no settings struct, no picker, no - // SetOrganizationSettings. - ProbeURL: "https://api.clerk.com/v1/users?limit=1", - // No NewNameResolver: the Backend API exposes no instance/application - // name endpoint reachable with a secret key, so the source keeps its - // generic name (the source-name worker degrades gracefully). - NewDriver: func(_ context.Context, c *http.Client, _ *coredata.Connector, _ *log.Logger) (drivers.Driver, error) { - return drivers.NewClerkDriver(c), nil - }, - } -} diff --git a/pkg/connector/provider/clerk_test.go b/pkg/connector/provider/clerk_test.go deleted file mode 100644 index 42696d062..000000000 --- a/pkg/connector/provider/clerk_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package provider_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.gearno.de/kit/httpclient" - "go.probo.inc/probo/pkg/accessreview/drivers" - "go.probo.inc/probo/pkg/connector/provider" - "go.probo.inc/probo/pkg/coredata" -) - -func TestClerkRegistration(t *testing.T) { - t.Parallel() - - r := provider.NewBuiltinRegistry() - reg, ok := r.Get(coredata.ConnectorProviderClerk) - require.True(t, ok, "clerk provider must be registered") - - assert.Equal(t, "Clerk", reg.DisplayName) - assert.True(t, reg.SupportsAPIKey) - assert.Equal(t, "", reg.APIKeyHeader) - assert.False(t, reg.APIKeyBasicAuth) - require.NotNil(t, reg.NewDriver, "clerk NewDriver closure must be wired") - - drv, err := reg.NewDriver( - context.Background(), - httpclient.DefaultClient(httpclient.WithSSRFProtection()), - &coredata.Connector{Provider: coredata.ConnectorProviderClerk}, - nil, - ) - require.NoError(t, err) - assert.IsType(t, &drivers.ClerkDriver{}, drv) -} diff --git a/pkg/coredata/connector_provider.go b/pkg/coredata/connector_provider.go index 9e6222b11..25f7e0d0c 100644 --- a/pkg/coredata/connector_provider.go +++ b/pkg/coredata/connector_provider.go @@ -58,32 +58,40 @@ const ( ConnectorProviderAsana ConnectorProvider = "ASANA" ConnectorProviderNetlify ConnectorProvider = "NETLIFY" ConnectorProviderClickUp ConnectorProvider = "CLICKUP" - ConnectorProviderClerk ConnectorProvider = "CLERK" - ConnectorProviderVercel ConnectorProvider = "VERCEL" - ConnectorProviderMonday ConnectorProvider = "MONDAY" - ConnectorProviderMetabase ConnectorProvider = "METABASE" - ConnectorProviderTailscale ConnectorProvider = "TAILSCALE" - ConnectorProviderAnthropic ConnectorProvider = "ANTHROPIC" - ConnectorProviderCursor ConnectorProvider = "CURSOR" - ConnectorProviderDatadog ConnectorProvider = "DATADOG" - ConnectorProviderOkta ConnectorProvider = "OKTA" - ConnectorProviderZendesk ConnectorProvider = "ZENDESK" - ConnectorProviderQovery ConnectorProvider = "QOVERY" - ConnectorProviderRender ConnectorProvider = "RENDER" - ConnectorProviderNeon ConnectorProvider = "NEON" - ConnectorProviderMercury ConnectorProvider = "MERCURY" - ConnectorProviderApollo ConnectorProvider = "APOLLO" - ConnectorProviderDeepgram ConnectorProvider = "DEEPGRAM" - ConnectorProviderClickHouse ConnectorProvider = "CLICKHOUSE" - ConnectorProviderLangfuse ConnectorProvider = "LANGFUSE" - ConnectorProviderPylon ConnectorProvider = "PYLON" - ConnectorProviderOpenRouter ConnectorProvider = "OPENROUTER" - ConnectorProviderIncidentIO ConnectorProvider = "INCIDENT_IO" - ConnectorProviderBrevo ConnectorProvider = "BREVO" - ConnectorProviderScaleway ConnectorProvider = "SCALEWAY" - ConnectorProviderYousign ConnectorProvider = "YOUSIGN" - ConnectorProviderRailway ConnectorProvider = "RAILWAY" - ConnectorProviderCrisp ConnectorProvider = "CRISP" + // ConnectorProviderClerk is retained for existing connectors but is + // no longer a registerable access-review provider: Clerk's Backend API + // (secret key) only exposes the customer's application end-users, not + // the Clerk workspace/dashboard team who administer the platform, so a + // campaign reviews the wrong population. Kept in IsValid and the + // GraphQL enum so stored CLERK rows still validate and serialize; + // dropped from ConnectorProviders and unregistered from the builtin + // registry so it cannot be added or fetched. + ConnectorProviderClerk ConnectorProvider = "CLERK" + ConnectorProviderVercel ConnectorProvider = "VERCEL" + ConnectorProviderMonday ConnectorProvider = "MONDAY" + ConnectorProviderMetabase ConnectorProvider = "METABASE" + ConnectorProviderTailscale ConnectorProvider = "TAILSCALE" + ConnectorProviderAnthropic ConnectorProvider = "ANTHROPIC" + ConnectorProviderCursor ConnectorProvider = "CURSOR" + ConnectorProviderDatadog ConnectorProvider = "DATADOG" + ConnectorProviderOkta ConnectorProvider = "OKTA" + ConnectorProviderZendesk ConnectorProvider = "ZENDESK" + ConnectorProviderQovery ConnectorProvider = "QOVERY" + ConnectorProviderRender ConnectorProvider = "RENDER" + ConnectorProviderNeon ConnectorProvider = "NEON" + ConnectorProviderMercury ConnectorProvider = "MERCURY" + ConnectorProviderApollo ConnectorProvider = "APOLLO" + ConnectorProviderDeepgram ConnectorProvider = "DEEPGRAM" + ConnectorProviderClickHouse ConnectorProvider = "CLICKHOUSE" + ConnectorProviderLangfuse ConnectorProvider = "LANGFUSE" + ConnectorProviderPylon ConnectorProvider = "PYLON" + ConnectorProviderOpenRouter ConnectorProvider = "OPENROUTER" + ConnectorProviderIncidentIO ConnectorProvider = "INCIDENT_IO" + ConnectorProviderBrevo ConnectorProvider = "BREVO" + ConnectorProviderScaleway ConnectorProvider = "SCALEWAY" + ConnectorProviderYousign ConnectorProvider = "YOUSIGN" + ConnectorProviderRailway ConnectorProvider = "RAILWAY" + ConnectorProviderCrisp ConnectorProvider = "CRISP" ) var ( @@ -123,7 +131,6 @@ func ConnectorProviders() []ConnectorProvider { ConnectorProviderAsana, ConnectorProviderNetlify, ConnectorProviderClickUp, - ConnectorProviderClerk, ConnectorProviderVercel, ConnectorProviderMonday, ConnectorProviderMetabase,