Link connector docs in the Add Source dialog

The access review Add Source dialog listed each connector with no
path to its setup documentation. Connectors that have a published
docs page on probo.com now surface a "Documentation" link on the
card, opening the page in a new tab; connectors without a page show
nothing extra.

The link is data-driven from the connector registry: a new
DocumentationURL on the provider Registration, populated for the 12
documented providers via a single accessReviewDocsURL helper, is
surfaced as a nullable documentationUrl on ConnectorProviderInfo and
rendered by the console only when present. This keeps the registry
the single source of truth and adds no client-side provider map.

The links resolve once the probo.com access-review docs pages are
deployed; until then they 404, so deploy the docs alongside this
change.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
Aurélien Sibiril
2026-07-24 11:49:59 +02:00
parent b963730433
commit b1a67aba00
18 changed files with 130 additions and 43 deletions

View File

@@ -31,9 +31,10 @@ import (
func anthropicRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderAnthropic,
DisplayName: "Anthropic",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderAnthropic,
DisplayName: "Anthropic",
DocumentationURL: accessReviewDocsURL("anthropic"),
SupportsAPIKey: true,
// Anthropic's Admin API authenticates with the admin key in the
// x-api-key header; it rejects Authorization: Bearer and returns
// 400 when both headers are present. APIKeyHeader makes the

View File

@@ -31,9 +31,10 @@ import (
func apolloRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderApollo,
DisplayName: "Apollo.io",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderApollo,
DisplayName: "Apollo.io",
DocumentationURL: accessReviewDocsURL("apollo"),
SupportsAPIKey: true,
// Apollo's REST API authenticates with a master API key in the
// x-api-key header; it rejects Authorization: Bearer (and, since
// Sept 2024, query/body key params). APIKeyHeader makes the

View File

@@ -31,10 +31,11 @@ import (
func cloudflareRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderCloudflare,
DisplayName: "Cloudflare",
ProbeURL: "https://api.cloudflare.com/client/v4/user/tokens/verify",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderCloudflare,
DisplayName: "Cloudflare",
DocumentationURL: accessReviewDocsURL("cloudflare"),
ProbeURL: "https://api.cloudflare.com/client/v4/user/tokens/verify",
SupportsAPIKey: true,
NewDriver: func(_ context.Context, c *http.Client, _ *coredata.Connector, _ *log.Logger) (drivers.Driver, error) {
return drivers.NewCloudflareDriver(c), nil
},

View File

@@ -32,8 +32,9 @@ import (
func crispRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderCrisp,
DisplayName: "Crisp",
Provider: coredata.ConnectorProviderCrisp,
DisplayName: "Crisp",
DocumentationURL: accessReviewDocsURL("crisp"),
// Model B: the plugin token is Probo's own Crisp Marketplace plugin
// credential, held server-side in bootstrap config, not pasted by
// the customer. ManagedAPIKey injects it at connect time; the

View File

@@ -33,9 +33,10 @@ const cursorMembersEndpoint = "https://api.cursor.com/teams/members"
func cursorRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderCursor,
DisplayName: "Cursor",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderCursor,
DisplayName: "Cursor",
DocumentationURL: accessReviewDocsURL("cursor"),
SupportsAPIKey: true,
// Cursor's Admin API has no third-party OAuth2 flow; it
// authenticates with a team admin key (key_...) presented as the
// HTTP Basic auth username with an empty password ("-u <key>:")

View File

@@ -31,9 +31,10 @@ import (
func deepgramRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderDeepgram,
DisplayName: "Deepgram",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderDeepgram,
DisplayName: "Deepgram",
DocumentationURL: accessReviewDocsURL("deepgram"),
SupportsAPIKey: true,
// Deepgram authenticates with an API key under the `Token` scheme
// (`Authorization: Token <key>`), not Bearer. APIKeyAuthScheme makes
// the APIKeyConnection use that scheme. There is no third-party

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// 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
// accessReviewDocsBaseURL is the public probo.com docs root for access-review
// connectors; each documented provider's page lives at this base + its slug.
const accessReviewDocsBaseURL = "https://www.probo.com/docs/product/access-review/"
// accessReviewDocsURL builds the public documentation URL for an access-review
// connector from its page slug (e.g. "anthropic"). The slug is passed explicitly
// by each registration rather than derived from the provider enum, so an enum
// containing an underscore (e.g. BETTER_STACK) cannot produce a wrong URL.
func accessReviewDocsURL(slug string) string {
return accessReviewDocsBaseURL + slug
}

View File

@@ -31,10 +31,11 @@ import (
func openaiRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderOpenAI,
DisplayName: "OpenAI",
ProbeURL: "https://api.openai.com/v1/models",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderOpenAI,
DisplayName: "OpenAI",
DocumentationURL: accessReviewDocsURL("openai"),
ProbeURL: "https://api.openai.com/v1/models",
SupportsAPIKey: true,
NewDriver: func(_ context.Context, c *http.Client, _ *coredata.Connector, _ *log.Logger) (drivers.Driver, error) {
return drivers.NewOpenAIDriver(c), nil
},

View File

@@ -31,9 +31,10 @@ import (
func openrouterRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderOpenRouter,
DisplayName: "OpenRouter",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderOpenRouter,
DisplayName: "OpenRouter",
DocumentationURL: accessReviewDocsURL("openrouter"),
SupportsAPIKey: true,
// OpenRouter authenticates with an organization management
// (provisioning) API key presented as Authorization: Bearer, the
// default APIKeyConnection scheme. The key is bound to one

View File

@@ -31,9 +31,10 @@ import (
func railwayRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderRailway,
DisplayName: "Railway",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderRailway,
DisplayName: "Railway",
DocumentationURL: accessReviewDocsURL("railway"),
SupportsAPIKey: true,
// Railway authenticates with an account API token as Authorization:
// Bearer. A single GraphQL call resolves the account's workspaces and
// their members, so there is nothing to pick (Pattern 3). Railway

View File

@@ -31,10 +31,11 @@ import (
func resendRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderResend,
DisplayName: "Resend",
ProbeURL: "https://api.resend.com/domains",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderResend,
DisplayName: "Resend",
DocumentationURL: accessReviewDocsURL("resend"),
ProbeURL: "https://api.resend.com/domains",
SupportsAPIKey: true,
NewDriver: func(_ context.Context, c *http.Client, _ *coredata.Connector, _ *log.Logger) (drivers.Driver, error) {
return drivers.NewResendDriver(c), nil
},

View File

@@ -32,9 +32,10 @@ import (
func scalewayRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderScaleway,
DisplayName: "Scaleway",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderScaleway,
DisplayName: "Scaleway",
DocumentationURL: accessReviewDocsURL("scaleway"),
SupportsAPIKey: true,
// Scaleway authenticates with the secret key in the X-Auth-Token header
// rather than Authorization: Bearer. APIKeyHeader makes the
// APIKeyConnection send that header and omit Authorization. The key is

View File

@@ -39,6 +39,10 @@ type Registration struct {
// Identity.
Provider coredata.ConnectorProvider
DisplayName string
// DocumentationURL is the public probo.com docs page for connecting this
// provider as an access source. Empty for providers with no doc page yet;
// surfaced (nullable) on ConnectorProviderInfo so the console renders a link.
DocumentationURL string
// OAuth2 metadata.
AuthURL string

View File

@@ -31,9 +31,10 @@ import (
func yousignRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderYousign,
DisplayName: "Yousign",
SupportsAPIKey: true,
Provider: coredata.ConnectorProviderYousign,
DisplayName: "Yousign",
DocumentationURL: accessReviewDocsURL("yousign"),
SupportsAPIKey: true,
// Yousign authenticates with an API key as Authorization: Bearer. The
// key is bound to one organization, so GET /v3/users returns everyone
// with nothing to pick (Pattern 3). The connector targets Yousign

View File

@@ -607,9 +607,15 @@ func (r *queryResolver) AccessReviewDrivers(ctx context.Context) ([]*types.Conne
)
}
var documentationURL *string
if reg.DocumentationURL != "" {
documentationURL = new(reg.DocumentationURL)
}
infos = append(infos, &types.ConnectorProviderInfo{
Provider: provider,
DisplayName: reg.DisplayName,
DocumentationURL: documentationURL,
OauthConfigured: oauthConfigured,
APIKeySupported: apiKeySupported,
APIKeyManaged: apiKeyManaged,

View File

@@ -104,6 +104,11 @@ enum ConnectorProvider
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!
"""