Skip to content

feat(gateway): add ClickHouse HTTP query connector with static query support#212

Merged
seanhanca merged 5 commits intomainfrom
feat/support-clickhouse-query-connector
Mar 26, 2026
Merged

feat(gateway): add ClickHouse HTTP query connector with static query support#212
seanhanca merged 5 commits intomainfrom
feat/support-clickhouse-query-connector

Conversation

@seanhanca
Copy link
Copy Markdown
Contributor

@seanhanca seanhanca commented Mar 23, 2026

Summary

  • Wire upstreamStaticBody through the connector template pipeline — adds the field to the TypeScript loader interface, JSON schema, admin template creation route, and seed script so endpoints can send a pre-configured static request body to the upstream service.
  • Add clickhouse-query connector template — a new config-only connector that proxies SQL queries to any ClickHouse instance via its HTTP query interface with Basic auth, SELECT-only enforcement, and caching.
  • Add how-to guide with dashboard visualization example — documents how to create and configure the connector, lists all 4 endpoints, and shows how to use /network_prices data to populate dashboard widgets (pricing table + distribution chart).

Endpoints

Endpoint Method Description
/ping GET ClickHouse health check
/tables GET List all tables in the default database
/query POST Dynamic SELECT-only queries (body = raw SQL)
/network_prices POST Pre-configured static query for orchestrator pricing data

Files changed

File Change
plugins/service-gateway/connectors/loader.ts Add upstreamStaticBody to TypeScript interface
plugins/service-gateway/connectors/connector-template.schema.json Add upstreamStaticBody to JSON schema
apps/web-next/src/app/api/v1/gw/admin/templates/route.ts Pass upstreamStaticBody during template instantiation
bin/seed-public-connectors.ts Pass upstreamStaticBody during seed
plugins/service-gateway/connectors/clickhouse-query.json New connector template
plugins/service-gateway/docs/clickhouse-query-connector.md New how-to guide
plugins/service-gateway/docs/connector-catalog.md Add clickhouse-query entry

Test plan

  • GET /ping → returns Ok.
  • GET /tables → returns table list in JSON format
  • POST /query with SELECT 1 FORMAT JSON → returns { "test_value": 1 }
  • POST /network_prices → returns 31 rows of orchestrator pricing data
  • Pre-push validation passes (273 SDK tests, build check, Vercel safety)

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added ClickHouse Query connector (query, network_prices, ping, tables) with caching and SELECT-only enforcement.
    • Connector templates, admin creation, and seeding now support a static upstream request body for endpoints (required when bodyTransform is "static").
  • Bug Fixes / Security

    • Prevents consumer Authorization headers from being forwarded when a connector uses Basic auth.
  • Documentation

    • Added ClickHouse connector guide and updated connector catalog.
  • Tests

    • Added test covering upstream header behavior with Basic auth.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
naap-platform Ready Ready Preview, Comment Mar 26, 2026 8:12pm

Request Review

@github-actions github-actions Bot added the size/L Large PR (201-500 lines) label Mar 23, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 23, 2026

⚠️ This PR is large (436 lines changed). Consider splitting it into smaller PRs for easier review.

@github-actions github-actions Bot added scope/shell Shell app changes scope/infra Infrastructure changes labels Mar 23, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds optional endpoint-level upstreamStaticBody (schema, types, persistence, seeding), a ClickHouse Query connector and docs, and changes gateway header injection to prevent forwarding consumer Authorization when connector auth is basic (with a test).

Changes

Cohort / File(s) Summary
Schema & Types
plugins/service-gateway/connectors/connector-template.schema.json, plugins/service-gateway/connectors/loader.ts
Add endpoints[].upstreamStaticBody (string, maxLength 65536); require it when bodyTransform === "static"; update ConnectorTemplateEndpoint type with upstreamStaticBody?: string.
Endpoint Creation & Seeding
apps/web-next/src/app/api/v1/gw/admin/templates/route.ts, bin/seed-public-connectors.ts
Persist upstreamStaticBody when creating endpoints from templates and when seeding public connectors (upstreamStaticBody: ep.upstreamStaticBody ?? null).
Gateway Transformation Logic & Tests
apps/web-next/src/lib/gateway/transform.ts, apps/web-next/src/lib/gateway/__tests__/transform.test.ts
When connector authType is basic, remove/skip consumer Authorization header before auth injection; added test verifying injected Basic auth and that consumer token is not forwarded.
ClickHouse Connector Definition
plugins/service-gateway/connectors/clickhouse-query.json
Add clickhouse-query connector (basic auth) with endpoints: POST /query (passthrough, SELECT-only regex + blacklist), POST /network_prices (static SQL), GET /ping, GET /tables; includes timeouts, health check, caching.
Documentation & Catalog
plugins/service-gateway/docs/clickhouse-query-connector.md, plugins/service-gateway/docs/connector-catalog.md
Add ClickHouse connector docs and catalog entry; document endpoints, transforms, SELECT-only constraints, examples, and security considerations.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Gateway
    participant SecretsStore
    participant Upstream
    Client->>Gateway: HTTP request (may include consumer Authorization + body)
    Gateway->>SecretsStore: Resolve connector secrets (username/password)
    SecretsStore-->>Gateway: Return credentials
    Gateway->>Gateway: buildUpstreamHeaders (remove consumer Authorization when authType = basic)
    Gateway->>Upstream: Forward request with injected Basic Authorization and final body
    Upstream-->>Gateway: Response
    Gateway-->>Client: Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • eliteprox
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a ClickHouse HTTP query connector with support for static query bodies, which is the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/support-clickhouse-query-connector

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
plugins/service-gateway/connectors/connector-template.schema.json (1)

188-192: Add conditional validation for static body transforms.

Recommend requiring upstreamStaticBody when bodyTransform is static to prevent invalid templates at authoring time.

Schema hardening example
       "items": {
         "type": "object",
         "required": ["name", "method", "path", "upstreamPath"],
         "additionalProperties": false,
         "properties": {
           ...
           "upstreamStaticBody": {
             "type": "string",
             "maxLength": 65536,
             "description": "Static body to send to upstream (used with bodyTransform: static)"
           }
-        }
+        },
+        "allOf": [
+          {
+            "if": { "properties": { "bodyTransform": { "const": "static" } } },
+            "then": { "required": ["upstreamStaticBody"] }
+          }
+        ]
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/service-gateway/connectors/connector-template.schema.json` around
lines 188 - 192, Add a conditional JSON Schema rule so that when the property
bodyTransform is set to "static" the upstreamStaticBody property is required and
validated (e.g., non-empty string / maxLength preserved). Implement an if/then
block in the connector-template.schema.json using "if":
{"properties":{"bodyTransform":{"const":"static"}}} and "then":
{"required":["upstreamStaticBody"],
"properties":{"upstreamStaticBody":{"type":"string","minLength":1,"maxLength":65536}}}
to enforce the dependency between bodyTransform and upstreamStaticBody.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web-next/src/app/api/v1/gw/admin/templates/route.ts`:
- Line 121: The line setting upstreamStaticBody uses the || null coalescing
which will convert empty strings to null and mismatches the seeding behavior;
change the expression that assigns upstreamStaticBody (the property in the
object returned/constructed in route.ts) from using || null to the nullish
coalescing operator ?? null so empty strings are preserved and behavior matches
bin/seed-public-connectors.ts.

In `@plugins/service-gateway/connectors/clickhouse-query.json`:
- Around line 18-20: The manifest currently lists authConfig and secretRefs with
two Basic-auth refs ("username","password") but uses a single envKey mapping
which writes the same value to both refs; update the connector schema and seed
logic to support per-secret environment mappings (e.g. add an envKeys object
like envKeys: { "username": "<ENV_VAR>", "password": "<ENV_VAR>" }) and change
the seed/loader that consumes envKey to prefer envKeys when present (fall back
to previous behavior only when secretRefs length == 1); ensure the code paths
handling "authConfig", "secretRefs" and the envKey-based seeding are updated to
map each secretRef to its corresponding envKeys entry instead of duplicating one
envKey for both secrets.
- Around line 13-15: Remove the hardcoded "allowedHosts" entry from the
connector JSON so host validation derives from the chosen "upstreamBaseUrl" at
runtime; specifically, delete the "allowedHosts":
["s7kh1yt2go.us-east-2.aws.clickhouse.cloud"] line in the clickhouse-query.json
so template overrides of "upstreamBaseUrl" won't be blocked and host
allowlisting is determined dynamically.

---

Nitpick comments:
In `@plugins/service-gateway/connectors/connector-template.schema.json`:
- Around line 188-192: Add a conditional JSON Schema rule so that when the
property bodyTransform is set to "static" the upstreamStaticBody property is
required and validated (e.g., non-empty string / maxLength preserved). Implement
an if/then block in the connector-template.schema.json using "if":
{"properties":{"bodyTransform":{"const":"static"}}} and "then":
{"required":["upstreamStaticBody"],
"properties":{"upstreamStaticBody":{"type":"string","minLength":1,"maxLength":65536}}}
to enforce the dependency between bodyTransform and upstreamStaticBody.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 78c3ae3a-33c3-479b-a1bc-634b2f8e0bf6

📥 Commits

Reviewing files that changed from the base of the PR and between 2accd67 and 6e940b1.

📒 Files selected for processing (7)
  • apps/web-next/src/app/api/v1/gw/admin/templates/route.ts
  • bin/seed-public-connectors.ts
  • plugins/service-gateway/connectors/clickhouse-query.json
  • plugins/service-gateway/connectors/connector-template.schema.json
  • plugins/service-gateway/connectors/loader.ts
  • plugins/service-gateway/docs/clickhouse-query-connector.md
  • plugins/service-gateway/docs/connector-catalog.md

Comment thread apps/web-next/src/app/api/v1/gw/admin/templates/route.ts Outdated
Comment thread plugins/service-gateway/connectors/clickhouse-query.json
Comment on lines +18 to +20
"authConfig": { "usernameRef": "username", "passwordRef": "password" },
"secretRefs": ["username", "password"],
"streamingEnabled": false,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

envKey mapping is incompatible with two Basic-auth secret refs.

secretRefs requires username and password, but envKey provides only one value. In current seed flow, that single value is written to both refs, which can break auth setup.

Suggested direction
-  "envKey": "CLICKHOUSE_QUERY_API_KEY"
+  "envKey": null

Or introduce per-secret env mapping in schema/seed logic (e.g. envKeys: { "username": "...", "password": "..." }).

Also applies to: 65-65

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/service-gateway/connectors/clickhouse-query.json` around lines 18 -
20, The manifest currently lists authConfig and secretRefs with two Basic-auth
refs ("username","password") but uses a single envKey mapping which writes the
same value to both refs; update the connector schema and seed logic to support
per-secret environment mappings (e.g. add an envKeys object like envKeys: {
"username": "<ENV_VAR>", "password": "<ENV_VAR>" }) and change the seed/loader
that consumes envKey to prefer envKeys when present (fall back to previous
behavior only when secretRefs length == 1); ensure the code paths handling
"authConfig", "secretRefs" and the envKey-based seeding are updated to map each
secretRef to its corresponding envKeys entry instead of duplicating one envKey
for both secrets.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/web-next/src/lib/gateway/__tests__/transform.test.ts (2)

199-200: Minor: Empty request init object is unnecessary.

The empty object can be removed for cleaner code:

🧹 Suggested cleanup
-      const request = new Request('https://example.com', {
-      });
+      const request = new Request('https://example.com');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web-next/src/lib/gateway/__tests__/transform.test.ts` around lines 199 -
200, The test creates a Request with an unnecessary empty init object; update
the Request instantiation in transform.test.ts (the const request variable) to
call new Request('https://example.com') without the second argument to clean up
the code.

205-205: Remove stale assertion checking for non-existent value.

The string 'gw_not_forwarded' doesn't appear anywhere in this test's setup. This assertion always passes trivially and adds confusion about what's being tested. Consider removing it or replacing it with an assertion that tests actual values from the test setup.

🧹 Suggested cleanup
       const expected = `Basic ${Buffer.from('admin:secret').toString('base64')}`;
       expect(result.headers.get('Authorization')).toBe(expected);
-      expect(result.headers.get('Authorization')).not.toContain('gw_not_forwarded');
       expect(result.headers.get('Authorization')).not.toContain('consumer-style-token');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web-next/src/lib/gateway/__tests__/transform.test.ts` at line 205,
Remove the stale assertion that checks for the non-existent string
'gw_not_forwarded' on the Authorization header; find the line with
expect(result.headers.get('Authorization')).not.toContain('gw_not_forwarded')
and either delete it or replace it with a meaningful assertion that verifies the
actual expected Authorization value from the test setup (e.g., assert equality
or that it contains the real token/header fragment), keeping the check focused
on result.headers.get('Authorization').
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web-next/src/lib/gateway/__tests__/transform.test.ts`:
- Around line 199-200: The test creates a Request with an unnecessary empty init
object; update the Request instantiation in transform.test.ts (the const request
variable) to call new Request('https://example.com') without the second argument
to clean up the code.
- Line 205: Remove the stale assertion that checks for the non-existent string
'gw_not_forwarded' on the Authorization header; find the line with
expect(result.headers.get('Authorization')).not.toContain('gw_not_forwarded')
and either delete it or replace it with a meaningful assertion that verifies the
actual expected Authorization value from the test setup (e.g., assert equality
or that it contains the real token/header fragment), keeping the check focused
on result.headers.get('Authorization').

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6fd47ca3-ccf8-45d4-ae01-10712b9dc328

📥 Commits

Reviewing files that changed from the base of the PR and between 6e940b1 and 31679df.

📒 Files selected for processing (2)
  • apps/web-next/src/lib/gateway/__tests__/transform.test.ts
  • apps/web-next/src/lib/gateway/transform.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/service-gateway/connectors/connector-template.schema.json`:
- Around line 194-199: The JSON Schema's conditional uses "if": { "properties":
{ "bodyTransform": { "const": "static" } } } which passes when bodyTransform is
missing; update the "if" to require the presence of bodyTransform by adding
"required": ["bodyTransform"] inside that "if" (so the conditional only matches
when bodyTransform exists and equals "static"), leaving the "then" unchanged
(which requires "upstreamStaticBody"); target the allOf conditional block
referencing bodyTransform and upstreamStaticBody when making this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 802b4a5a-f40f-4b0e-968e-c478a6978423

📥 Commits

Reviewing files that changed from the base of the PR and between 7d54ec8 and 8467e0c.

📒 Files selected for processing (2)
  • apps/web-next/src/lib/gateway/__tests__/transform.test.ts
  • plugins/service-gateway/connectors/connector-template.schema.json

Comment thread plugins/service-gateway/connectors/connector-template.schema.json
seanhanca and others added 5 commits March 26, 2026 13:09
…support

Wire `upstreamStaticBody` through the connector template pipeline (loader
interface, JSON schema, admin template route, seed script) so endpoints
can send a pre-configured request body to the upstream service.

Add the `clickhouse-query` connector template with four endpoints:
  - /network_prices  — static SQL for orchestrator pricing data
  - /query           — dynamic SELECT-only queries with regex + blacklist
  - /ping            — ClickHouse health check
  - /tables          — list tables via SHOW TABLES

Include a how-to guide with dashboard visualization example and update
the connector catalog.

Made-with: Cursor
…ded with basic auth

Updated the buildUpstreamRequest and buildUpstreamHeaders functions to ensure that when the connector's authType is set to 'basic', the consumer's Authorization header is not forwarded to the upstream service. This change enhances security by preventing potential credential leaks. Added corresponding tests to verify this behavior.
- Use ?? null instead of || null for upstreamStaticBody to preserve
  empty strings and match the seed script's nullish coalescing
- Remove hardcoded allowedHosts from clickhouse-query.json so host
  validation derives from upstreamBaseUrl at runtime, avoiding
  conflicts when users override the base URL
- Remove envKey from clickhouse-query.json — a single env var cannot
  map to two separate Basic-auth secrets (username + password);
  users configure credentials via the Settings tab UI instead
- Add JSDoc docstrings to all exported interfaces and functions in
  the connector template loader and route handlers

Made-with: Cursor
- Add conditional JSON Schema validation requiring upstreamStaticBody
  when bodyTransform is "static"
- Remove empty Request init object in basic-auth test
- Remove stale gw_not_forwarded assertion that always passed trivially

Made-with: Cursor
Without "required": ["bodyTransform"] in the if-block, endpoints that
omit bodyTransform (defaulting to passthrough) would incorrectly pass
the condition and be forced to provide upstreamStaticBody.

Made-with: Cursor
@seanhanca seanhanca force-pushed the feat/support-clickhouse-query-connector branch from 16610ca to 32deb62 Compare March 26, 2026 20:10
@seanhanca seanhanca merged commit 1c09783 into main Mar 26, 2026
27 checks passed
@seanhanca seanhanca deleted the feat/support-clickhouse-query-connector branch March 26, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope/infra Infrastructure changes scope/shell Shell app changes size/L Large PR (201-500 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants