Skip to content

feat(gcp): tabbed Google Cloud dashboard with project, region and workload filters - #1496

Closed
JeremyFunk wants to merge 7 commits into
feat/gcp-integration-page-plainfrom
feat/gcp-dashboard-tabs
Closed

JeremyFunk wants to merge 7 commits into
feat/gcp-integration-page-plainfrom
feat/gcp-dashboard-tabs

Conversation

@JeremyFunk

@JeremyFunk JeremyFunk commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #1481.

What

  • Google Cloud template: one Services section, one tab per service: Cloud Run, Cloud Functions (1st gen), GKE, Compute Engine, Cloud SQL, Pub/Sub, Load Balancing.
  • Thin tabs filled out from metrics the poller already stores: Functions instances + memory, GKE memory in use + node memory, Compute Engine memory/network received/disk, Cloud SQL disk + replica lag, Pub/Sub sent/acked/dead-lettered/publish requests.
  • Variables: Project (cloud.account.id), Region (cloud.region), Workload (service.name), all with All.
    • A chart filters on a variable only if its series carry the attribute: Region skips Pub/Sub and load balancers (global), Workload only applies to charts grouped by workload.
    • The project_id template parameter now sets the Project filter's default.

New variable source: metric

Query variables could only list values from traces/logs. Metrics-only integrations never write those.

  • { kind: "metric", metricType, metricNames, scope: "metric" | "resource", attributeKey }: values of a data-point or resource attribute on the named metrics.
  • Engine: attributeValues takes metricNames, and scoped metric discovery reads ResourceAttributes for the resource scope. Raw-table read, same as the query-builder autocomplete.
  • Wired through: option query (options.ts, used by the board and by share links), web provider, getMetricAttributeValues, variables dialog (new "Metric attribute…" / "Metric resource attribute…" choices), v2 API schema.

Template previews

layout.y is relative to the tab, so the preview now shows the root canvas, then each section's first tab, stacked. The layout test checks rows per container.

Verification

  • vitest: query-engine (full package), widgets, domain http + variables, backend dashboards + templates.
  • Ran the three variables' compiled option SQL against local Tinybird holding the 2026-10-08 GCP e2e data: returned the project, us-central1/europe-west1, and the Cloud Run/GKE/VM workloads.
  • /lab/gcp-dashboard (new): the built template on the read-only canvas, with fixture series that follow the variable chips through each widget's own where clause. Checked with Playwright: tab switching, Region narrows GKE but leaves Pub/Sub alone, and the variables dialog shows and edits the metric source.
  • Not checked: the live board against the API. The lab fakes the option lists; the engine query behind them was checked against Tinybird (above).

Lab

/lab/gcp-dashboard reads apps/web/src/lab/gcp-dashboard-fixture.json, which is gcpTemplate.build({}) (web can't import @maple/backend). The regenerate command is in the lab file.

…kload filters

The Google Cloud template put every chart on one page. It now groups them into
one tab per service (Cloud Run, Cloud Functions, GKE, Compute Engine, Cloud SQL,
Pub/Sub, Load Balancing), fills out the thin tabs, and filters by Project,
Region and Workload.

Query variables could only list values from traces and logs, which a
metrics-only integration never writes. A new `metric` variable source lists a
data-point or resource attribute's values from named metrics of one type, read
from the raw metric table like the query-builder autocomplete. The option
query, the board's provider, the share path, the variables dialog and the v2
API all take it.

Template previews now show the root canvas and each section's first tab, since
a widget's layout is relative to its tab.
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e07fde82-0134-4492-839f-a579af44d491

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🟡 Confidence 6/10 · needs attention
Held at 6 because a security warning is open.
quality 90/100 · 1 warning · tests partial · risk medium

Reworks the Google Cloud template into one tab per service with project/region/workload variables, and adds a metric dashboard-variable source so metric-only boards can list attribute values. Safe to merge apart from the project-id filter escaping it drops.

  • gcpTemplate builds one Services section with seven service tabs
  • New metric variable source: metricNames + scope: metric|resource
  • metricScopedAttributeValuesQuery reads ResourceAttributes for resource scope
  • buildTemplatePreview stacks the root canvas plus each section's first tab

Findings

🟠 Warning · F1 · Project filter substitutes the project id unescaped into SQL

security · packages/backend/src/dashboard-templates/infrastructure/gcp.ts:79

This template used to build its project clause with escapeMetricStringLiteral(projectId); now the raw project_id template parameter is the project variable's default value (gcp.ts:465) and $project is substituted into the where clause verbatim — interpolateWhereClause replaces a reference with variable.value and does not escape (interpolate.ts:79-85). A project id containing " or \ therefore closes the literal and appends SQL to the metric query instead of returning no rows. Escape the value where it is taken in, as the other templates still do.

Set the default to the escaped value: import `escapeMetricStringLiteral` again and pass `defaultValue: projectId ? escapeMetricStringLiteral(projectId) : undefined`, or keep the clause a build-time literal when `projectId` is set.
🤖 Prompt to fix this finding with an AI agent
Findings from an automated review of commit c36c091eb2a119667c71c2a4a392135395997a2e. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F1 · Warning · security · packages/backend/src/dashboard-templates/infrastructure/gcp.ts:79
Project filter substitutes the project id unescaped into SQL
This template used to build its project clause with `escapeMetricStringLiteral(projectId)`; now the raw `project_id` template parameter is the `project` variable's default value (`gcp.ts:465`) and `$project` is substituted into the where clause verbatim — `interpolateWhereClause` replaces a reference with `variable.value` and does not escape (`interpolate.ts:79-85`). A project id containing `"` or `\` therefore closes the literal and appends SQL to the metric query instead of returning no rows. Escape the value where it is taken in, as the other templates still do.
Suggested fix: Set the default to the escaped value: import `escapeMetricStringLiteral` again and pass `defaultValue: projectId ? escapeMetricStringLiteral(projectId) : undefined`, or keep the clause a build-time literal when `projectId` is set.
What was checked
  • metricNames is capped at 20 and each GCP variable selects at most 8 metrics
  • resourceVariable metric type matches the stored kind for the names it picks
  • Variable keys cloud.account.id, cloud.region and service.name are real resource attributes (mapping.ts:37-75)

c36c091 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 inline note from Maple's review. The score and summary are in the review comment above.

}
}

const PROJECT_FILTER = `resource.cloud.account.id = "$project"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Project filter substitutes the project id unescaped into SQL

F1 · Warning · security

This template used to build its project clause with escapeMetricStringLiteral(projectId); now the raw project_id template parameter is the project variable's default value (gcp.ts:465) and $project is substituted into the where clause verbatim — interpolateWhereClause replaces a reference with variable.value and does not escape (interpolate.ts:79-85). A project id containing " or \ therefore closes the literal and appends SQL to the metric query instead of returning no rows. Escape the value where it is taken in, as the other templates still do.

Set the default to the escaped value: import `escapeMetricStringLiteral` again and pass `defaultValue: projectId ? escapeMetricStringLiteral(projectId) : undefined`, or keep the clause a build-time literal when `projectId` is set.
🤖 Prompt to fix with an AI agent
In `packages/backend/src/dashboard-templates/infrastructure/gcp.ts:79`: Project filter substitutes the project id unescaped into SQL.

This template used to build its project clause with `escapeMetricStringLiteral(projectId)`; now the raw `project_id` template parameter is the `project` variable's default value (`gcp.ts:465`) and `$project` is substituted into the where clause verbatim — `interpolateWhereClause` replaces a reference with `variable.value` and does not escape (`interpolate.ts:79-85`). A project id containing `"` or `\` therefore closes the literal and appends SQL to the metric query instead of returning no rows. Escape the value where it is taken in, as the other templates still do.

Suggested fix: Set the default to the escaped value: import `escapeMetricStringLiteral` again and pass `defaultValue: projectId ? escapeMetricStringLiteral(projectId) : undefined`, or keep the clause a build-time literal when `projectId` is set.

Verify the problem exists at that location before changing it, and keep the fix to those lines.

…series

/lab/gcp-dashboard renders the built template through the read-only canvas:
its tabs, the Project / Region / Workload chips over fixture options, and the
variables dialog. Series follow the chips through each widget's own where
clause, so a filter a widget leaves out (Region on Pub/Sub) changes nothing.

Also shortens the dialog's summary of a metric-sourced variable and formats
the template files.
@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🟡 Confidence 5/10 · needs attention
The template, query and v2 schema are pinned by tests, but the earlier unescaped $project substitution is unchanged at this head and the new metric variable path is untested end to end.
quality 80/100 · 2 warnings · tests partial · risk medium · 2/2 new units observable · 1 file not read

Reworks the Google Cloud template into one tabbed Services section with Project/Region/Workload variables, adding a metric dashboard-variable source through the engine, web provider and v2 API. Safe to merge except for the unescaped $project substitution still open from the earlier review; the 2,428-line lab fixture was not read line by line (its widget where clauses were sampled instead).

  • gcpTemplate becomes one tabbed Services section over seven GCP services
  • New metric variable source lists an attribute's values from named metrics
  • metricScopedAttributeValuesQuery accepts metricNames and a resource scope
  • Template previews stack the root canvas then each section's first tab

Findings

🟠 Warning · F2 · Metric names input has no 20-item cap the dashboard schema enforces

correctness · apps/web/src/components/dashboard-builder/config/variables-manager-dialog.tsx:553

V2DashboardVariableSource caps metricNames at 20 entries (packages/domain/src/http/v2/dashboards.ts:545; the widgets schema does the same), but this input splits on every comma with no cap and draftError only rejects empty names. A variable with 21 comma-separated names saves into a document the dashboard schema rejects, so the save fails with a length error that says nothing about the metric count.

Add a `> 20` check for `source.kind === "metric"` to `draftError`, beside the empty-name check at line 112, so the dialog refuses the draft before it reaches the schema.
🤖 Prompt to fix this finding with an AI agent
Findings from an automated review of commit 98522699a4e7135a834b1412ce2d55c7a1ffce77. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F2 · Warning · correctness · apps/web/src/components/dashboard-builder/config/variables-manager-dialog.tsx:553
Metric names input has no 20-item cap the dashboard schema enforces
`V2DashboardVariableSource` caps `metricNames` at 20 entries (`packages/domain/src/http/v2/dashboards.ts:545`; the widgets schema does the same), but this input splits on every comma with no cap and `draftError` only rejects empty names. A variable with 21 comma-separated names saves into a document the dashboard schema rejects, so the save fails with a length error that says nothing about the metric count.
Suggested fix: Add a `> 20` check for `source.kind === "metric"` to `draftError`, beside the empty-name check at line 112, so the dialog refuses the draft before it reaches the schema.

Still open from earlier reviews

What was checked
  • ResourceAttributes exists on all four metric tables, so the resource scope compiles for any metricType (datasources.ts:1421)
  • Region and Workload clauses are added only for regional groups and service.name groupings (gcp.ts:407-412), checked against each metric's stored resource attributes (gcp.test.ts:102-121)
  • metricNames is bounded 1-20 by the engine schema and rendered as escaped literals (query-engine.ts:485, attribute-keys.test.ts:192)
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
Metric-sourced variable option list (warehouse read of ResourceAttributes) DB query yes runs through the existing executeCHQuery span in packages/query-engine/src/runtime/query-engine.ts:2334
getMetricAttributeValues server function server function yes Effect.fn("QueryEngine.getMetricAttributeValues") at apps/web/src/api/warehouse/metrics.ts:262
Files not reviewed (1)

The review ended before it read these diffs, so nothing above vouches for them.

  • apps/web/src/lab/gcp-dashboard-fixture.json

9852269 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 inline note from Maple's review. The score and summary are in the review comment above.

source: {
...source,
// Empty entries stay while typing ("a, "); validation flags them.
metricNames: event.target.value.split(",").map((name) => name.trim()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Metric names input has no 20-item cap the dashboard schema enforces

F2 · Warning · correctness

V2DashboardVariableSource caps metricNames at 20 entries (packages/domain/src/http/v2/dashboards.ts:545; the widgets schema does the same), but this input splits on every comma with no cap and draftError only rejects empty names. A variable with 21 comma-separated names saves into a document the dashboard schema rejects, so the save fails with a length error that says nothing about the metric count.

Add a `> 20` check for `source.kind === "metric"` to `draftError`, beside the empty-name check at line 112, so the dialog refuses the draft before it reaches the schema.
🤖 Prompt to fix with an AI agent
In `apps/web/src/components/dashboard-builder/config/variables-manager-dialog.tsx:553`: Metric names input has no 20-item cap the dashboard schema enforces.

`V2DashboardVariableSource` caps `metricNames` at 20 entries (`packages/domain/src/http/v2/dashboards.ts:545`; the widgets schema does the same), but this input splits on every comma with no cap and `draftError` only rejects empty names. A variable with 21 comma-separated names saves into a document the dashboard schema rejects, so the save fails with a length error that says nothing about the metric count.

Suggested fix: Add a `> 20` check for `source.kind === "metric"` to `draftError`, beside the empty-name check at line 112, so the dialog refuses the draft before it reaches the schema.

Verify the problem exists at that location before changing it, and keep the fix to those lines.

…o feat/gcp-dashboard-tabs

# Conflicts:
#	packages/backend/src/dashboard-templates/infrastructure/gcp.ts
… into feat/gcp-dashboard-tabs

# Conflicts:
#	apps/web/src/routeTree.gen.ts
…ted on GCP

Google Cloud's resource detector sets faas.name on Cloud Run and Cloud
Functions, so those services were drawn as AWS Lambda, and GKE services as
plain Kubernetes. cloud.provider=gcp now classifies as a gcp platform with
the Google Cloud mark, tooltip, colour and legend entry.
@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🟡 Confidence 5/10 · needs attention
The engine and template additions are pinned by new tests, but the unescaped $project filter and the uncapped metric-name input are still open at this head.
quality 80/100 · 2 warnings · tests partial · risk medium · 1/1 new units observable

Tabbed Google Cloud dashboard template with Project / Region / Workload query variables over a new metric variable source, plus a gcp service-map platform. Engine work is tested; the two earlier warnings still stand, and the generated lab fixture was only partly read.

  • gcpTemplate gains Project/Region/Workload query variables and one tab per service
  • New metric variable source lists attribute values from named metrics of one metricType
  • Service map classifies and legends a gcp platform
  • buildTemplatePreview stacks the root canvas and each section's first tab

Still open from earlier reviews

What was checked
  • Where-clause interpolation substitutes variable values raw (interpolate.ts:73); share links constrain the charset (share-variables.ts:64)
  • Resource-scoped metric option query reads the raw metrics table via ResourceAttributes (attribute-keys.ts:156), pinned by attribute-keys.test.ts
  • gcp is classified before the Lambda rule in both the API and the local UI (query-engine.http.ts:259, use-local-service-map.ts:111)
Observability coverage: 1 of 1 changes observable
Change Kind Observable Evidence
Dashboard variable option list from named metrics (metric-scoped attributeValues query) warehouse read yes runs through executeCHQuery with context attributeValues:metric-scoped (runtime/query-engine.ts:2338)

e2ceedd · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@JeremyFunk

Copy link
Copy Markdown
Collaborator Author

Folded into #1335 (commit e5f5eb9): the tabbed template, metric query variables and the lab belong with the template PR rather than stacked on top of it.

@JeremyFunk JeremyFunk closed this Oct 15, 2026
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