Registration.ExtraSettings was a single flat list, but the API-key and client-credentials connect dialogs need different fields whenever a provider offers both paths, because a different create resolver and a different driver sits behind each. Replace it with APIKeyExtraSettings and ClientCredentialsExtraSettings, and split the GraphQL surface to match so a client cannot render one path's settings on the other. This fixes two connectors that could not be connected at all. 1Password declared accountId and region only, which are the client-credentials shape. The API-key dialog therefore rendered those two fields, mapAPIKeyExtraSettingToField returned nil for both so buildExtraFields discarded them, and the SCIM-bridge driver failed on an empty SCIMBridgeURL. The console already mapped scimBridgeUrl, but no registration declared that key, so the branch was dead. It now declares scimBridgeUrl on the API-key path and accountId + region on client credentials. Langfuse declared baseUrl as required, but mapAPIKeyExtraSettingToField had no LANGFUSE case, so buildExtraFields dropped the value the customer typed and the mutation failed with "langfuseBaseUrl is required". Every other extra-settings provider had a case. The GraphQL input field, the settings struct, the probe builder and the driver were all already correct; only the console mapping was missing. buildExtraFields now takes the settings list explicitly instead of reading it off the provider, so each dialog passes its own path's list and cannot silently iterate the other one. Register rejects a settings list for a path the provider does not offer, and an empty or duplicate setting key within one list. A key repeated across the two lists is allowed: that is how a dual-path provider declares a setting both dialogs need. The new resolver tests walk the whole chain the console walks, from the key a Registration declares through the mutation input field to the persisted settings struct, so a key renamed on one side and not the other fails in CI instead of at connect time. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
264 lines
9.7 KiB
GraphQL
264 lines
9.7 KiB
GraphQL
enum ConnectorProvider
|
|
@goModel(model: "go.probo.inc/probo/pkg/coredata.ConnectorProvider") {
|
|
SLACK @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSlack")
|
|
GOOGLE_WORKSPACE
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGoogleWorkspace"
|
|
)
|
|
LINEAR @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderLinear")
|
|
ONE_PASSWORD
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderOnePassword"
|
|
)
|
|
HUBSPOT
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderHubSpot")
|
|
DOCUSIGN
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderDocuSign")
|
|
GRAFANA
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGrafana")
|
|
NOTION @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderNotion")
|
|
BREX @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderBrex")
|
|
TALLY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderTally")
|
|
CLOUDFLARE
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderCloudflare")
|
|
OPENAI @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderOpenAI")
|
|
POSTHOG @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderPostHog")
|
|
SENTRY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSentry")
|
|
SIGNOZ
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSigNoz")
|
|
SUPABASE
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSupabase")
|
|
BETTER_STACK
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderBetterStack"
|
|
)
|
|
GITHUB @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGitHub")
|
|
INTERCOM
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderIntercom")
|
|
RESEND @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderResend")
|
|
SENDGRID
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSendGrid")
|
|
MICROSOFT_365
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderMicrosoft365"
|
|
)
|
|
GITLAB @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGitLab")
|
|
BITBUCKET
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderBitbucket")
|
|
HEROKU @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderHeroku")
|
|
PAGERDUTY
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderPagerDuty")
|
|
ASANA @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderAsana")
|
|
NETLIFY
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderNetlify")
|
|
CLICKUP
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderClickUp")
|
|
CLERK @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderClerk")
|
|
VERCEL @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderVercel")
|
|
MONDAY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderMonday")
|
|
METABASE
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderMetabase")
|
|
TAILSCALE
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderTailscale")
|
|
ANTHROPIC
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderAnthropic"
|
|
)
|
|
CURSOR @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderCursor")
|
|
DATADOG @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderDatadog")
|
|
OKTA @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderOkta")
|
|
ZENDESK @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderZendesk")
|
|
QOVERY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderQovery")
|
|
RENDER @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderRender")
|
|
NEON @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderNeon")
|
|
MERCURY
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderMercury")
|
|
APOLLO @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderApollo")
|
|
DEEPGRAM
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderDeepgram")
|
|
CLICKHOUSE
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderClickHouse"
|
|
)
|
|
LANGFUSE
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderLangfuse")
|
|
PYLON @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderPylon")
|
|
OPENROUTER
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderOpenRouter"
|
|
)
|
|
INCIDENT_IO
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderIncidentIO"
|
|
)
|
|
BREVO @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderBrevo")
|
|
SCALEWAY
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderScaleway")
|
|
YOUSIGN
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderYousign")
|
|
RAILWAY
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderRailway")
|
|
CRISP @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderCrisp")
|
|
DOTFILE
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderDotfile")
|
|
SEGMENT
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSegment")
|
|
SQUARE @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSquare")
|
|
GOOGLE_ANALYTICS
|
|
@goEnum(
|
|
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGoogleAnalytics"
|
|
)
|
|
}
|
|
|
|
type ConnectorProviderInfo {
|
|
provider: ConnectorProvider!
|
|
displayName: String!
|
|
"""
|
|
documentationUrl is the public probo.com docs page for connecting this
|
|
provider as an access source, or null when no doc page exists yet.
|
|
"""
|
|
documentationUrl: String
|
|
oauthConfigured: Boolean!
|
|
apiKeySupported: Boolean!
|
|
"""
|
|
apiKeyManaged is true when the provider connects with a Probo-supplied
|
|
API key (Model B, e.g. Crisp's marketplace plugin token) that the
|
|
operator has configured. The customer supplies only the extra settings
|
|
(e.g. a Website ID), not the key. False until the key is configured,
|
|
which keeps such a provider out of the catalog.
|
|
"""
|
|
apiKeyManaged: Boolean!
|
|
clientCredentialsSupported: Boolean!
|
|
oauth2Scopes: [String!]!
|
|
"""
|
|
apiKeyExtraSettings lists the per-provider settings the API-key connect
|
|
form must render and submit, in render order. Empty when the provider needs
|
|
none or has no API-key path.
|
|
"""
|
|
apiKeyExtraSettings: [ConnectorProviderSettingInfo!]!
|
|
"""
|
|
clientCredentialsExtraSettings lists the settings the client-credentials
|
|
connect form must render and submit. A provider offering both paths
|
|
(1Password) returns different fields here than in apiKeyExtraSettings,
|
|
because a different settings struct and driver sits behind each — so a
|
|
client cannot render one path's settings on the other.
|
|
"""
|
|
clientCredentialsExtraSettings: [ConnectorProviderSettingInfo!]!
|
|
}
|
|
|
|
type ConnectorProviderSettingInfo {
|
|
key: String!
|
|
label: String!
|
|
required: Boolean!
|
|
}
|
|
|
|
input ConnectorFilter {
|
|
providers: [ConnectorProvider!]
|
|
}
|
|
|
|
type Connector {
|
|
id: ID!
|
|
provider: ConnectorProvider!
|
|
oauth2Scopes: [String!]! @goField(forceResolver: true)
|
|
createdAt: Datetime!
|
|
}
|
|
|
|
type SlackConnection implements Node {
|
|
id: ID!
|
|
channel: String
|
|
channelId: String
|
|
createdAt: Datetime!
|
|
updatedAt: Datetime!
|
|
|
|
permission(action: String!): Boolean! @goField(forceResolver: true)
|
|
}
|
|
|
|
type SlackConnectionConnection {
|
|
edges: [SlackConnectionEdge!]!
|
|
pageInfo: PageInfo!
|
|
}
|
|
|
|
type SlackConnectionEdge {
|
|
cursor: CursorKey!
|
|
node: SlackConnection!
|
|
}
|
|
|
|
extend type Mutation {
|
|
createAPIKeyConnector(
|
|
input: CreateAPIKeyConnectorInput!
|
|
): CreateAPIKeyConnectorPayload!
|
|
createClientCredentialsConnector(
|
|
input: CreateClientCredentialsConnectorInput!
|
|
): CreateClientCredentialsConnectorPayload!
|
|
deleteConnector(input: DeleteConnectorInput!): DeleteConnectorPayload!
|
|
deleteSlackConnection(
|
|
input: DeleteSlackConnectionInput!
|
|
): DeleteSlackConnectionPayload!
|
|
}
|
|
|
|
input CreateAPIKeyConnectorInput {
|
|
organizationId: ID!
|
|
provider: ConnectorProvider!
|
|
"""
|
|
apiKey is the customer-supplied credential. It is null for
|
|
ManagedAPIKey (Model B) providers such as Crisp, where the server
|
|
injects Probo's own key and the customer provides only the extra
|
|
settings.
|
|
"""
|
|
apiKey: String
|
|
tallyOrganizationId: String
|
|
sentryOrganizationSlug: String
|
|
supabaseOrganizationSlug: String
|
|
githubOrganization: String
|
|
grafanaBaseUrl: String
|
|
signozBaseUrl: String
|
|
onePasswordScimBridgeUrl: String
|
|
metabaseInstanceUrl: String
|
|
posthogRegion: String
|
|
posthogInstanceUrl: String
|
|
oktaDomain: String
|
|
betterStackTeamName: String
|
|
qoveryOrganizationId: String
|
|
renderWorkspaceId: String
|
|
neonOrganizationId: String
|
|
langfuseBaseUrl: String
|
|
scalewayOrganizationId: String
|
|
crispWebsiteId: String
|
|
segmentRegion: String
|
|
}
|
|
|
|
type CreateAPIKeyConnectorPayload {
|
|
connector: Connector!
|
|
}
|
|
|
|
input CreateClientCredentialsConnectorInput {
|
|
organizationId: ID!
|
|
provider: ConnectorProvider!
|
|
clientId: String!
|
|
clientSecret: String!
|
|
tokenUrl: String!
|
|
scope: String
|
|
onePasswordAccountId: String
|
|
onePasswordRegion: String
|
|
}
|
|
|
|
type CreateClientCredentialsConnectorPayload {
|
|
connector: Connector
|
|
}
|
|
|
|
input DeleteConnectorInput {
|
|
connectorId: ID!
|
|
}
|
|
|
|
type DeleteConnectorPayload {
|
|
deletedConnectorId: ID!
|
|
}
|
|
|
|
input DeleteSlackConnectionInput {
|
|
slackConnectionId: ID!
|
|
}
|
|
|
|
type DeleteSlackConnectionPayload {
|
|
deletedSlackConnectionId: ID!
|
|
}
|