Skip to content

Add Google Analytics, Dotfile, Segment and Square access-review connectors#1482

Open
aureliensibiril wants to merge 10 commits into
mainfrom
aureliensibiril/ga4-dotfile-segment-square
Open

Add Google Analytics, Dotfile, Segment and Square access-review connectors#1482
aureliensibiril wants to merge 10 commits into
mainfrom
aureliensibiril/ga4-dotfile-segment-square

Conversation

@aureliensibiril

@aureliensibiril aureliensibiril commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds four access-review connectors — two OAuth2 and two API-key — plus their logos. Continues the connector-provider Registry pattern (one file per provider in pkg/connector/provider/); every provider declares a connection probe.

  • Google Analytics (GA4) — OAuth2 with both analytics.readonly and analytics.manage.users.readonly (readonly alone 403s on the accounts list). Enumerates v1alpha access bindings at the account level and per property, merging entries by lowercased email so a user with account- and property-scoped roles appears once. Manual account picker (Pattern 1) with SetOrganizationSettings, a live account fetch, a name resolver, and a per-connection probe. Distinct from the Google Workspace connector.
  • Dotfile — API key in the X-DOTFILE-API-KEY header (Pattern 3). Lists members via GET /v1/users (page pagination, include_suspended=true); owner/admin ⇒ admin, suspended_at ⇒ inactive. Static probe.
  • Segment (Twilio) — Public API token as Bearer (Pattern 3) plus a required Region setting (US or EU) resolved to the regional host (api.segmentapis.com / eu1.api.segmentapis.com) and stored as SegmentConnectorSettings.BaseURL. Lists members via GET /users, then a per-user GET /users/{id} for roles (an unavoidable N+1), and GET /invites for pending members surfaced as inactive. Workspace Owner ⇒ admin. Per-connection BuildProbeURL.
  • Square — OAuth2 (EMPLOYEES_READ) or a personal access token (Pattern 3). POST /v2/team-members/search with the Square-Version header and cursor pagination returns email/status/is_owner directly, so is_owner ⇒ admin with no role resolution. Custom probe (/v2/merchants/me) and name resolver.

Surfaces touched

  • coredata — provider enum values (+ IsValid/ConnectorProviders), SegmentConnectorSettings{base_url} and GoogleAnalyticsConnectorSettings{account_id} (Dotfile/Square are Pattern 3, no settings struct), one migration adding the four ADD VALUEs.
  • connector/provider — four Registrations + builtin.go wiring; buildSegmentProbeURL and probeSquare in probe.go; Square and GA4 name resolvers; GA4 organizations fetch + providerOrgConfigs picker entry.
  • GraphQLConnectorProvider enum @goEnum lines + a segmentRegion input on CreateAPIKeyConnectorInput, validated and mapped to the regional base URL server-side.
  • Bootstrap — GA4 and Square added to the confidential-OAuth provider loop and validateRequired; .env.example documents their CLIENT_ID/CLIENT_SECRET.
  • Frontend — four brand logos wired into ThirdPartyLogo; the add-source dialog maps Segment's region onto the segmentRegion API-key input (without the mapping the required setting is silently dropped and the create is rejected).

Tests

Cassette-backed driver tests for all four (synthetic *.example.com fixtures), plus unit tests for the Segment probe URL and the bootstrap OAuth provider list. The probe-coverage test walks the registry asserting every provider declares a probe.

Notes for reviewers

  • Frontend generated artifacts (schema.graphql, Relay __generated__) are gitignored; CI's make generate + Relay compile regenerate them from the committed connector.graphql source (which carries segmentRegion and the four @goEnum enum values).
  • GA4 and Square each need a registered OAuth app; credentials are supplied per-deployment via the bootstrap env vars in .env.example.

Summary by cubic

Adds four access-review connectors: Google Analytics (GA4), Dotfile, Segment, and Square. Improves reliability and safety: Dotfile retries transient errors; GA4/Segment escape path segments; GA4 reviews include subproperties; Segment members keep unknown active status.

Service +1755 -0

  • Added drivers:
    • GA4 (OAuth): merges account- and property-level bindings by email; includes subproperties; name resolver; organizations picker; path segments escaped.
    • Dotfile (API key X-DOTFILE-API-KEY): lists users; owner/admin ⇒ admin; suspended ⇒ inactive; retries transient 5xx.
    • Segment (Bearer + region base URL): lists users, per-user roles (/users/{id}), and pending invites (inactive). Confirmed members have unknown active status. Path segments escaped.
    • Square (OAuth or PAT): searches team members with Square-Version; is_owner ⇒ admin; custom probe; name resolver.
  • Provider registry: registrations for all four; Segment BuildProbeURL; Square probe; GA4 and Square name resolvers.
  • Bootstrap: GA4 and Square added to the confidential OAuth provider list and required-secret checks.

Coredata +102 -53

  • Providers added: DOTFILE, SEGMENT, SQUARE, GOOGLE_ANALYTICS (enum, IsValid, ConnectorProviders), with migration.
  • New settings: SegmentConnectorSettings{base_url}, GoogleAnalyticsConnectorSettings{account_id}.

GraphQL API +39 -0

  • Exposes new providers in ConnectorProvider.
  • Adds segmentRegion to CreateAPIKeyConnectorInput; server maps US/EU to base URL.
  • GA4 picker: list accounts (status checked before decode) and return selected account id.

Tests +399 -1

  • Cassette-backed driver tests for GA4, Dotfile, Segment, and Square.
  • Unit tests for Segment probe URL, and Square and GA4 name resolvers.
  • VCR scrubs X-Dotfile-Api-Key from recorded cassettes.

App: console +3 -0

  • Maps Segment “region” UI field to segmentRegion API input.

Package: ui +87 -0

  • Adds logos and ThirdPartyLogo wiring for Dotfile, Google Analytics, Segment, and Square.

Other +5 -0

  • .env.example: GA4 and Square OAuth client variables.

Written for commit 04459e7. Summary will update on new commits.

Review in cubic

…ctors

Two OAuth2 and two API-key connectors:

- Google Analytics (GA4): OAuth2 with both analytics.readonly and
  analytics.manage.users.readonly (readonly alone 403s on the accounts
  list); v1alpha accessBindings enumerated at account and property level
  and merged by email; manual account picker (Pattern 1) with a
  per-connection probe and name resolver; distinct from Google Workspace.
- Dotfile: API key in the X-DOTFILE-API-KEY header (Pattern 3); GET
  /v1/users (owner/admin, suspended_at) with a static probe.
- Segment (Twilio): Public API token as Bearer with a required Region
  setting (US or EU) mapped to the regional host; GET /users plus per-user
  GET /users/{id} for roles and /invites for pending members; per-connection
  BuildProbeURL.
- Square: OAuth2 (EMPLOYEES_READ) or a personal access token (Pattern 3);
  POST /v2/team-members/search returns email/status/is_owner directly, so no
  role resolution; custom probe and name resolver.

Google Analytics and Square are confidential OAuth clients, wired into the
bootstrap OAuth provider list and .env.example. Segment carries a required
extra setting, so the console add-source dialog maps region onto its
segmentRegion API-key input; without that mapping the value is silently
dropped and the create is rejected.

Cassette-backed driver tests plus unit tests for the Segment probe URL and
the bootstrap OAuth provider list.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 37 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread pkg/accessreview/drivers/google_analytics.go Outdated
Comment thread pkg/accessreview/drivers/organizations.go
Comment thread pkg/accessreview/drivers/segment.go Outdated
The GA4 driver passed the account and property IDs to url.JoinPath as raw
segments, and the Segment driver built its per-user endpoint by
concatenating the user ID into url.URL.Path — both bypass the url.PathEscape
rule that every sibling driver (and the matching name resolvers) already
follow. The IDs are numeric today so there is no behaviour change, but this
keeps the drivers consistent and safe if a provider ever returns a segment
with a reserved character.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The shared VCR BeforeSave hook scrubs every other header-auth provider's key
(X-Api-Key, Api-Key, Signoz-Api-Key, X-Auth-Token) but was not updated for
Dotfile, so re-recording testdata/dotfile.yaml with a real X-DOTFILE-API-KEY
would persist the key into the committed cassette. Delete the canonicalized
X-Dotfile-Api-Key header alongside the others.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Cover the 2xx business/display-name path, the terminal non-2xx branches
(401/403/404/500 keep the generic source name), the Square-Version header,
and Google Analytics' empty-account-id short-circuit, matching the existing
resolver tests.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
listProperties filtered properties with parent:accounts/{id}, which returns
only properties whose direct parent is the account and silently drops
subproperties and roll-up properties (parented to another property). A member
holding a binding only on such a subproperty was omitted from the review.
Switch to the ancestor:accounts/{id} filter, which walks the whole account
hierarchy and is a strict superset, so no property is lost.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Segment's /users API exposes no active/suspended field, so reporting every
confirmed member as Active=true fabricated a status the source never
provides, contrary to the AccountRecord contract (nil = no explicit signal).
Leave Active nil for confirmed members; pending invites keep Active=false,
which is a real signal from /invites.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
ListGoogleAnalyticsOrganizations decoded the response body into the success
struct before inspecting the HTTP status, unlike every other lister in the
file. Check the status first so a non-2xx no longer wastes a decode against
an error body and the ordering matches the sibling functions.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The Square, Segment and Google Analytics drivers all wrap their transport
with retryRoundTripper for flaky 5xx responses, but the Dotfile driver used
the client directly. Wrap it the same way so all four connectors handle
transient upstream failures consistently.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
ExternalID is normally a stable provider-side ID, not an email. GA4 access
bindings identify a user only by email — no per-user ID and no display name
are exposed — so email is the only stable key available. Document that on
googleAnalyticsRecords so the choice reads as deliberate.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant