Add five API-key access-review connectors

Add Mercury, Apollo.io, Deepgram, ClickHouse Cloud, and Langfuse as
access-review connectors. All are API-key, single-tenant providers
(Pattern 3): the key identifies one tenant, so there is no OAuth flow,
picker UI, or bootstrap/helm configuration.

- Mercury: Bearer token, GET /api/v1/users, cursor pagination.
- Apollo.io: x-api-key header, GET /api/v1/users/search (teammates).
- Deepgram: Token scheme; lists members across every project and
  dedupes by member_id, unioning per-project scopes.
- ClickHouse Cloud: HTTP Basic (keyId:keySecret); discovers the org
  via GET /v1/organizations, then lists its members.
- Langfuse: HTTP Basic (publicKey:secretKey); a base-URL setting
  selects the regional cloud host or a self-hosted instance.

Each adds the enum value, migration, GraphQL binding, provider
Registration, a driver with a cassette-driven test, and a brand logo.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
Aurélien Sibiril
2026-06-12 19:53:13 +02:00
parent bd6a470d6d
commit 6a285a59b9
41 changed files with 2245 additions and 1 deletions

View File

@@ -0,0 +1,37 @@
---
# Hand-authored fixture for GET /api/v1/users/search against an Apollo.io
# account (teammates, not the prospect DB). The user object shape (id, name,
# first_name, last_name, email, role) and the pagination wrapper mirror the
# documented response. Synthetic IDs/emails only.
version: 2
interactions:
- id: 0
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: api.apollo.io
form:
page:
- "1"
per_page:
- "100"
headers:
Accept:
- application/json
url: https://api.apollo.io/api/v1/users/search?page=1&per_page=100
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"users":[{"id":"5f0000000000000000000001","name":"Alice Admin","first_name":"Alice","last_name":"Admin","email":"alice@example.com","role":"Admin"},{"id":"5f0000000000000000000002","name":"Bob Rep","first_name":"Bob","last_name":"Rep","email":"bob@example.com","role":"Sales Rep"},{"id":"5f0000000000000000000003","name":"Carol Manager","first_name":"Carol","last_name":"Manager","email":"carol@example.com","role":"Billing and Seat Manager"},{"id":"5f0000000000000000000004","name":"","first_name":"","last_name":"","email":"dave@example.com","role":"Sales Rep"}],"pagination":{"page":1,"per_page":100,"total_entries":4,"total_pages":1}}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 140ms

View File

@@ -0,0 +1,58 @@
---
# Hand-authored fixture for the ClickHouse Cloud member-listing flow: GET
# /v1/organizations (key maps to exactly one org) then GET
# /v1/organizations/{id}/members. Responses use the {status,requestId,result}
# envelope. assignedRoles is the live role source; the deprecated `role` field
# is the fallback. Synthetic IDs/emails only.
version: 2
interactions:
- id: 0
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: api.clickhouse.cloud
headers:
Accept:
- application/json
url: https://api.clickhouse.cloud/v1/organizations
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"status":200,"requestId":"11111111-1111-4111-8111-111111111111","result":[{"id":"aaaaaaaa-0000-4000-8000-000000000001","name":"Example Org","createdAt":"2025-12-01T08:00:00Z"}]}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 110ms
- id: 1
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: api.clickhouse.cloud
headers:
Accept:
- application/json
url: https://api.clickhouse.cloud/v1/organizations/aaaaaaaa-0000-4000-8000-000000000001/members
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"status":200,"requestId":"22222222-2222-4222-8222-222222222222","result":[{"userId":"u-0000-0001","name":"Admin User","email":"admin@example.com","role":"admin","joinedAt":"2026-01-02T10:00:00Z","assignedRoles":[{"roleId":"33333333-3333-4333-8333-333333333333","roleName":"Admin","roleType":"system"}]},{"userId":"u-0000-0002","name":"Dev User","email":"dev@example.com","role":"developer","joinedAt":"2026-02-03T11:00:00Z","assignedRoles":[]},{"userId":"u-0000-0003","name":"","email":"custom@example.com","role":"developer","joinedAt":"2026-03-04T12:00:00Z","assignedRoles":[{"roleId":"44444444-4444-4444-8444-444444444444","roleName":"Billing Admin","roleType":"custom"}]}]}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 110ms

View File

@@ -0,0 +1,82 @@
---
# Hand-authored fixture for the Deepgram account-listing flow: GET /v1/projects
# then GET /v1/projects/{project_id}/members for each project. owner@example.com
# belongs to both projects (with different scopes), exercising the dedup +
# scope-union path. Synthetic IDs/emails only.
version: 2
interactions:
- id: 0
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: api.deepgram.com
headers:
Accept:
- application/json
url: https://api.deepgram.com/v1/projects
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"projects":[{"project_id":"proj-1111-1111","name":"Production"},{"project_id":"proj-2222-2222","name":"Staging"}]}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 90ms
- id: 1
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: api.deepgram.com
headers:
Accept:
- application/json
url: https://api.deepgram.com/v1/projects/proj-1111-1111/members
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"members":[{"member_id":"m-0000-0000-0001","email":"owner@example.com","first_name":"Olivia","last_name":"Owner","scopes":["member"]},{"member_id":"m-0000-0000-0002","email":"member@example.com","first_name":"Mia","last_name":"Member","scopes":["member"]}]}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 90ms
- id: 2
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: api.deepgram.com
headers:
Accept:
- application/json
url: https://api.deepgram.com/v1/projects/proj-2222-2222/members
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"members":[{"member_id":"m-0000-0000-0001","email":"owner@example.com","first_name":"Olivia","last_name":"Owner","scopes":["owner"]},{"member_id":"m-0000-0000-0003","email":"dev@example.com","first_name":"Dan","last_name":"Dev","scopes":["member"]}]}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 90ms

View File

@@ -0,0 +1,32 @@
---
# Hand-authored fixture for GET /api/public/organizations/memberships against a
# Langfuse organization (org-scoped API key). The membership object shape
# (userId, role, email, name) and the {memberships:[...]} wrapper mirror the
# documented response. Synthetic IDs/emails only.
version: 2
interactions:
- id: 0
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: cloud.langfuse.com
headers:
Accept:
- application/json
url: https://cloud.langfuse.com/api/public/organizations/memberships
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"memberships":[{"userId":"lf-user-1","role":"OWNER","email":"owner@example.com","name":"Olivia Owner"},{"userId":"lf-user-2","role":"ADMIN","email":"admin@example.com","name":"Adam Admin"},{"userId":"lf-user-3","role":"MEMBER","email":"member@example.com","name":"Mia Member"},{"userId":"lf-user-4","role":"VIEWER","email":"viewer@example.com","name":""}]}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 100ms

View File

@@ -0,0 +1,35 @@
---
# Hand-authored fixture for GET /api/v1/users against a Mercury organization.
# The user object shape (userId, firstName, lastName, email, organizationRole)
# mirrors the documented UserDetails schema; the page cursor is absent so the
# driver stops after one page. Synthetic IDs/emails only.
version: 2
interactions:
- id: 0
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
host: api.mercury.com
form:
limit:
- "500"
headers:
Accept:
- application/json
url: https://api.mercury.com/api/v1/users?limit=500
method: GET
response:
proto: HTTP/2.0
proto_major: 2
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"users":[{"userId":"8f1a6f1e-0000-4000-8000-000000000001","firstName":"Ada","lastName":"Admin","email":"ada@example.com","organizationRole":"administrator"},{"userId":"8f1a6f1e-0000-4000-8000-000000000002","firstName":"Ben","lastName":"Books","email":"ben@example.com","organizationRole":"bookkeeper"},{"userId":"8f1a6f1e-0000-4000-8000-000000000003","firstName":"Eve","lastName":"Employee","email":"eve@example.com","organizationRole":"employee"}],"page":{"nextPage":null,"previousPage":null}}'
headers:
Content-Type:
- application/json
status: 200 OK
code: 200
duration: 120ms