Skip to content

feat(google-analytics): add Google Analytics GA4 integration#3600

Open
waleedlatif1 wants to merge 1 commit intostagingfrom
waleedlatif1/google-analytics-integration
Open

feat(google-analytics): add Google Analytics GA4 integration#3600
waleedlatif1 wants to merge 1 commit intostagingfrom
waleedlatif1/google-analytics-integration

Conversation

@waleedlatif1
Copy link
Collaborator

Summary

  • Add Google Analytics GA4 integration with 3 operations: Run Report, Run Realtime Report, Get Metadata
  • Register OAuth provider with analytics.readonly scope under the Google provider
  • Add block, tools, icon, and docs

Type of Change

  • New feature

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link

vercel bot commented Mar 15, 2026

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

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Mar 15, 2026 10:45am

Request Review

@cursor
Copy link

cursor bot commented Mar 15, 2026

PR Summary

Medium Risk
Introduces a new OAuth provider and makes authenticated requests to Google Analytics APIs; main risk is correctness of OAuth scopes/redirects and JSON parsing of user-provided filter inputs.

Overview
Adds a new google_analytics integration end-to-end: a block with selectable operations (run report, run realtime report, get metadata), corresponding tool implementations calling the GA4 Data API (runReport, runRealtimeReport, metadata), and registration in the block/tool registries.

Extends OAuth support by registering the google-analytics provider/service (including analytics.readonly scope) and adds a GoogleAnalyticsIcon plus docs page + docs metadata entries; also updates several existing tool docs to include pricing/rateLimit/metadata fields and adjusts some Grain/Jina docs outputs.

Written by Cursor Bugbot for commit 83f3be8. Configure here.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 15, 2026

Greptile Summary

This PR adds a Google Analytics GA4 integration with three operations — Run Report, Run Realtime Report, and Get Metadata — following the established patterns of other Google integrations (OAuth provider registration, block config, tool files, icon, and docs).

The integration is largely well-structured and consistent with the rest of the codebase, but there are two issues worth addressing before merging:

  • JSON.parse without error handling (run_report.ts, run_realtime_report.ts): dimensionFilter, metricFilter, and orderBys are all parsed with bare JSON.parse() calls inside the body builder function. Any malformed JSON from a user will throw an unhandled SyntaxError rather than a descriptive tool error.
  • Non-deterministic OAuth account ID (auth.ts): getUserInfo generates id: \${profile.sub}-${crypto.randomUUID()}`, appending a fresh UUID on every authentication. This mirrors the existing google-adspattern, but it preventsbetterAuthfrom matching returning users to their existing credential records, causing duplicate database entries to accumulate on each reconnect. If multiple-accounts-per-user is the intent, a clarifying comment would help; otherwiseprofile.sub` alone should be used as the stable identifier.

Confidence Score: 2/5

  • Not safe to merge until the JSON.parse crash paths and the duplicate-credential auth issue are resolved.
  • Two logic bugs were found: bare JSON.parse calls that will throw unhandled SyntaxErrors on user input, and a non-deterministic OAuth account ID that creates duplicate credential records on every re-authentication. The integration is otherwise well-structured and follows existing patterns.
  • apps/sim/tools/google_analytics/run_report.ts, apps/sim/tools/google_analytics/run_realtime_report.ts, apps/sim/lib/auth/auth.ts

Important Files Changed

Filename Overview
apps/sim/tools/google_analytics/run_report.ts New tool for running GA4 reports. Contains a critical bug: JSON.parse() is called on user-supplied dimensionFilter, metricFilter, and orderBys strings without try-catch, causing unhandled SyntaxErrors on invalid JSON input.
apps/sim/tools/google_analytics/run_realtime_report.ts New tool for GA4 realtime reports. Same JSON.parse-without-try-catch bug as run_report.ts for dimensionFilter and metricFilter.
apps/sim/lib/auth/auth.ts Registers a new google-analytics OAuth provider. getUserInfo appends crypto.randomUUID() to profile.sub, which generates a different account ID on every re-authentication and will create duplicate credential records in the database.
apps/sim/tools/google_analytics/get_metadata.ts New tool to fetch GA4 property metadata (available dimensions and metrics). Clean implementation with proper response mapping and error handling.
apps/sim/tools/google_analytics/types.ts TypeScript interfaces for all three GA4 tool params and responses. Well-typed with appropriate optional fields and union type for the combined response.
apps/sim/blocks/blocks/google_analytics.ts Block configuration with three operations, correct OAuth wiring, and wand prompts for AI-assisted field completion. Operation-conditional field rendering is handled correctly. Params function properly coerces string inputs (limit, offset, etc.) to numbers.
apps/sim/lib/oauth/oauth.ts Registers google-analytics provider with correct analytics.readonly scope alongside userinfo scopes. Consistent with other Google provider registrations.
apps/sim/lib/oauth/types.ts Adds google-analytics to OAuthProvider and OAuthService union types — straightforward and correct.

Sequence Diagram

sequenceDiagram
    participant User
    participant Block as GoogleAnalyticsBlock
    participant OAuth as betterAuth (google-analytics)
    participant Tool as GA Tool (run_report / run_realtime_report / get_metadata)
    participant GAAPI as Google Analytics Data API

    User->>Block: Configure operation + propertyId + fields
    Block->>OAuth: Request OAuth access token
    OAuth-->>Block: accessToken (via google-analytics provider)
    Block->>Tool: Invoke selected tool with params + accessToken
    Tool->>Tool: Build request body (JSON.parse filters/orderBys)
    Tool->>GAAPI: POST /v1beta/properties/{propertyId}:runReport
    GAAPI-->>Tool: JSON response (rows, headers, metadata)
    Tool->>Tool: transformResponse → structured output
    Tool-->>Block: { dimensionHeaders, metricHeaders, rows, rowCount, metadata }
    Block-->>User: Display output
Loading

Comments Outside Diff (3)

  1. apps/sim/tools/google_analytics/run_report.ts, line 1572-1580 (link)

    Unhandled JSON.parse exceptions for user-provided filter/sort strings

    JSON.parse() is called on dimensionFilter, metricFilter, and orderBys without a try-catch. If the user supplies malformed JSON, a SyntaxError will propagate unhandled out of the body function, crashing the tool execution with no useful error message.

    The same issue exists in apps/sim/tools/google_analytics/run_realtime_report.ts for dimensionFilter and metricFilter (lines 1356–1360 of that file).

  2. apps/sim/tools/google_analytics/run_realtime_report.ts, line 1355-1360 (link)

    Unhandled JSON.parse exceptions

    Both JSON.parse(params.dimensionFilter) and JSON.parse(params.metricFilter) are called without error handling. Invalid JSON provided by the user will throw an unhandled SyntaxError. This is the same issue as in run_report.ts — wrap each call in a try-catch and surface a descriptive error message.

  3. apps/sim/lib/auth/auth.ts, line 1036 (link)

    Random UUID appended to user ID causes a new credential record on every re-auth

    id: \${profile.sub}-${crypto.randomUUID()}`generates a different ID every time the same user authenticates via this provider.betterAuthuses the value returned fromgetUserInfo.id` as the account lookup key; because it changes on each flow, no existing account record is ever matched and a new one is created for every authentication attempt. This leads to accumulating stale credential rows in the database and users losing access to previously connected Google Analytics accounts.

    The same pattern appears to be used in the existing google-ads provider — if that behaviour is intentional (e.g., to allow multiple GA properties per Google account), please add a comment explaining the design decision. Otherwise, use just profile.sub as the stable, unique identifier for this Google account.

Last reviewed commit: 83f3be8

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

| `apiKey` | string | Yes | Firecrawl API key |
| `pricing` | custom | No | No description |
| `metadata` | string | No | No description |
| `rateLimit` | string | No | No description |
Copy link

Choose a reason for hiding this comment

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

Unrelated tool docs modified with "No description" entries

Medium Severity

This PR adds pricing, metadata, and rateLimit parameters with "No description" to the docs of many unrelated tools (Firecrawl, Google Books, Google Maps, Google PageSpeed, Google Translate, Linkup, Perplexity, Serper, Jina). These appear to be auto-generated side effects from running a docs generation script, not intentional changes for a Google Analytics feature PR. The "No description" entries degrade documentation quality for those tools.

Additional Locations (2)
Fix in Cursor Fix in Web

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