Skip to content

fix(google_analytics): migrate page-opened trigger from deprecated UA…#20046

Merged
luancazarine merged 2 commits intomasterfrom
fix/google_analytics
Feb 18, 2026
Merged

fix(google_analytics): migrate page-opened trigger from deprecated UA…#20046
luancazarine merged 2 commits intomasterfrom
fix/google_analytics

Conversation

@maycon-pipedream
Copy link
Copy Markdown
Collaborator

@maycon-pipedream maycon-pipedream commented Feb 16, 2026

Summary

Migrate the google_analytics-page-opened trigger from the deprecated Universal Analytics (UA) Reporting API v4 to the Google Analytics Data API (GA4).

Problem

The trigger fails with a 404 Not Found error when attempting to deploy:

Error 404: The requested URL /v4/reports:batchGet was not found on this server.
Analytics_new_page_opened_trigger_error

Root Cause

The component uses the Google Analytics Reporting API v4 (Universal Analytics), which was officially sunset by Google on July 1, 2024. The reports.batchGet endpoint no longer exists on Google's servers.

Affected code path:

  • page-opened.mjs calls this.analytics.queryReports(data)
  • google_analytics.app.mjs executes client.reports.batchGet(data) using analyticsreporting v4

Impact

  • 100% of trigger deploys fail
  • Users cannot monitor page views via Google Analytics

Solution

Updated the trigger to use the Google Analytics Data API (GA4), which is the current supported API.
Select_the_Property

Connection_success

Changes

Aspect Before (UA) After (GA4)
Input prop viewId property
API method queryReports() queryReportsGA4()
Metric ga:pageviews screenPageViews
Request structure reportRequests[] dateRanges[] + metrics[]
Response structure reports[0].data.totals[0].values[0] rows[0].metricValues[0].value

Breaking Changes

Existing users will need to:

  1. Reconfigure the trigger with a GA4 property instead of a View ID
  2. Ensure they have a Google Analytics 4 property configured

References

Summary by CodeRabbit

  • Updates

    • "New Page Opened" source now uses the GA4 Data API and is renamed to "New Page Opened (GA4)".
    • Configuration property renamed from "viewId" to "property".
    • Page view reporting updated to GA4 metrics and response handling.
  • Chores

    • Package version bumped to 0.2.2.

@maycon-pipedream maycon-pipedream self-assigned this Feb 16, 2026
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 16, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
pipedream-docs-redirect-do-not-edit Ignored Ignored Feb 16, 2026 8:29pm

Request Review

@pipedream-component-development
Copy link
Copy Markdown
Collaborator

Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified.

@pipedream-component-development
Copy link
Copy Markdown
Collaborator

Thanks for submitting this PR! When we review PRs, we follow the Pipedream component guidelines. If you're not familiar, here's a quick checklist:

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 16, 2026

Walkthrough

Bumps the Google Analytics package version and migrates the "New Page Opened" source from Universal Analytics (GA3) to the GA4 Data API, updating public props, query shape, metrics, response parsing, and metadata/event emission.

Changes

Cohort / File(s) Summary
Version Update
components/google_analytics/package.json
Package version bumped from 0.2.1 to 0.2.2.
GA4 Migration — Page Opened Source
components/google_analytics/sources/page-opened/page-opened.mjs
Source metadata/version updated (0.1.1 → 0.2.0; name → "New Page Opened (GA4)"); prop key viewId renamed to property and propDefinition updated; replaces GA3 reportRequests with analytics.queryReportsGA4 using propertyId, dateRanges, and screenPageViews metric; response parsing switched to GA4 shapes (rows[0].metricValues[0].value or totals[0].metricValues[0].value with "0" fallback) and integer comparison for pageViews; event payload/meta updated accordingly.

Sequence Diagram

sequenceDiagram
    participant Source as Page-Opened Source
    participant GA4 as GA4 Data API
    participant Emitter as Event Emitter

    Source->>GA4: queryReportsGA4(propertyId, dateRanges, metrics)
    activate GA4
    GA4-->>Source: GA4 report response (rows / totals)
    deactivate GA4

    Source->>Source: parse response (rows[0].metricValues... or totals[0].metricValues...)
    Source->>Source: extract pageViews (fallback "0") and parseInt
    Source->>Source: compare current vs previous pageViews

    alt pageViews > previousPageViews
        Source->>Emitter: emit(event, report, meta)
        activate Emitter
        Emitter-->>Source: acknowledgment
        deactivate Emitter
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: migrating a Google Analytics trigger from deprecated Universal Analytics to GA4, which aligns with the file changes and PR objectives.
Description check ✅ Passed The description provides a comprehensive overview with WHY context (API deprecation), problem statement (404 error), root cause, solution, detailed change table, breaking changes, and references—far exceeding the minimal template requirement.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/google_analytics

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
Contributor

@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: 2

🤖 Fix all issues with AI agents
In `@components/google_analytics/sources/page-opened/page-opened.mjs`:
- Line 84: The condition using parseInt lacks an explicit radix; update the
parseInt calls for pageViews and previousPageViews in the if condition (the line
comparing previousPageViews and pageViews) to specify a radix (e.g.,
parseInt(pageViews, 10) and parseInt(previousPageViews, 10)) so parsing is
deterministic; alternatively, replace both with Number(pageViews) and
Number(previousPageViews) if you prefer clearer numeric conversion.
- Around line 58-82: Remove trailing whitespace in the affected block where
propertyId, report, previousPageViews, and pageViews are defined (the lines
around the propertyId assignment, the analytics.queryReportsGA4 call, and the
pageViews fallback expression) and ensure the file ends with a single newline
character; run a quick linter/trim to strip trailing spaces and add the missing
EOF newline so CI passes.

|| "0";

if (!previousPageViews || pageViews > previousPageViews) {
if (!previousPageViews || parseInt(pageViews) > parseInt(previousPageViews)) {
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.

🧹 Nitpick | 🔵 Trivial

Specify radix for parseInt.

Best practice is to always pass the radix to parseInt to avoid ambiguity.

♻️ Proposed fix
-      if (!previousPageViews || parseInt(pageViews) > parseInt(previousPageViews)) {
+      if (!previousPageViews || parseInt(pageViews, 10) > parseInt(previousPageViews, 10)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!previousPageViews || parseInt(pageViews) > parseInt(previousPageViews)) {
if (!previousPageViews || parseInt(pageViews, 10) > parseInt(previousPageViews, 10)) {
🤖 Prompt for AI Agents
In `@components/google_analytics/sources/page-opened/page-opened.mjs` at line 84,
The condition using parseInt lacks an explicit radix; update the parseInt calls
for pageViews and previousPageViews in the if condition (the line comparing
previousPageViews and pageViews) to specify a radix (e.g., parseInt(pageViews,
10) and parseInt(previousPageViews, 10)) so parsing is deterministic;
alternatively, replace both with Number(pageViews) and Number(previousPageViews)
if you prefer clearer numeric conversion.

@luancazarine luancazarine moved this from Ready for PR Review to In Review in Component (Source and Action) Backlog Feb 16, 2026
Copy link
Copy Markdown
Collaborator

@luancazarine luancazarine left a comment

Choose a reason for hiding this comment

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

Hi @maycon-pipedream, please fix the lint errors

@luancazarine luancazarine moved this from In Review to Changes Required in Component (Source and Action) Backlog Feb 16, 2026
Copy link
Copy Markdown
Contributor

@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

🤖 Fix all issues with AI agents
Before applying any fix, first verify the finding against the current code and
decide whether a code change is actually needed. If the finding is not valid or
no change is required, do not modify code for that item and briefly explain why
it was skipped.

In `@components/google_analytics/sources/page-opened/page-opened.mjs`:
- Around line 80-82: The fallback using report?.totals is dead because the
runReport request doesn't include metricAggregations; update the runReport
request construction (the object passed to analyticsDataClient.runReport) to
include metricAggregations: ["TOTAL"] so report.totals will be populated, and
then keep the pageViews computation (report?.rows?.. || report?.totals?.. ||
"0") as-is; alternatively, if you prefer removing dead code, delete the
report?.totals fallback from the pageViews assignment that reads from report to
rely solely on report?.rows and "0".

Comment on lines +80 to +82
const pageViews = report?.rows?.[0]?.metricValues?.[0]?.value
|| report?.totals?.[0]?.metricValues?.[0]?.value
|| "0";
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.

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the actual code file to see the request construction
cat -n components/google_analytics/sources/page-opened/page-opened.mjs | sed -n '60,85p'

Repository: PipedreamHQ/pipedream

Length of output: 921


🌐 Web query:

GA4 Data API runReport metricAggregations totals documentation

💡 Result:

To request totals (and other metric aggregations) in the GA4 Google Analytics Data API runReport response, you must set the metricAggregations field on the request.

Request (RunReportRequest)

Include one or more MetricAggregation enums in metricAggregations, e.g. TOTAL (sum), MINIMUM, MAXIMUM, COUNT. When you do this, the API notes that aggregated values can appear in rows whose dimensionValues are set to RESERVED_(MetricAggregation) (for example, RESERVED_TOTAL). [1][3]

Example:

POST https://analyticsdata.googleapis.com/v1beta/properties/PROPERTY_ID:runReport
{
  "dateRanges": [{"startDate": "2025-01-01", "endDate": "2025-01-31"}],
  "dimensions": [{"name": "country"}],
  "metrics": [{"name": "activeUsers"}],
  "metricAggregations": ["TOTAL"]
}

[1][3]

Response (RunReportResponse)

If requested, the response can include:

  • totals[] = totaled metric values
  • maximums[] = max metric values
  • minimums[] = min metric values
    [2]

Where this is documented

  • metricAggregations field on properties.runReport (RunReportRequest): [1]
  • totals[] field on RunReportResponse: [2]
  • MetricAggregation enum values (TOTAL, MINIMUM, MAXIMUM, COUNT): [3]
  • A commonly-cited practical note: add "TOTAL" to metricAggregations to get totals: [4]

Sources: [1] https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport, [2] https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/RunReportResponse, [3] https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/MetricAggregation, [4] https://stackoverflow.com/questions/75869046/how-to-include-the-totals-field-for-batchrunreports-from-ga4


The totals fallback on line 81 is dead code without metricAggregations in the request.

Per GA4 Data API documentation, the runReport response only populates totals when metricAggregations (e.g., ["TOTAL"]) is explicitly included in the request. The request on lines 61–76 omits this field, so report?.totals will always be empty/undefined, making the fallback on line 81 unreachable. While harmless, either add metricAggregations to the request to make the fallback functional, or remove it for clarity.

♻️ Option A: Add metricAggregations to the request
       const report = await this.analytics.queryReportsGA4({
         property: propertyId,
         data: {
           dateRanges: [
             {
               startDate,
               endDate: "today",
             },
           ],
           metrics: [
             {
               name: "screenPageViews",
             },
           ],
+          metricAggregations: [
+            "TOTAL",
+          ],
         },
       });
♻️ Option B: Remove the dead fallback
-      const pageViews = report?.rows?.[0]?.metricValues?.[0]?.value
-        || report?.totals?.[0]?.metricValues?.[0]?.value
-        || "0";
+      const pageViews = report?.rows?.[0]?.metricValues?.[0]?.value || "0";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const pageViews = report?.rows?.[0]?.metricValues?.[0]?.value
|| report?.totals?.[0]?.metricValues?.[0]?.value
|| "0";
const pageViews = report?.rows?.[0]?.metricValues?.[0]?.value || "0";
🤖 Prompt for AI Agents
Before applying any fix, first verify the finding against the current code and
decide whether a code change is actually needed. If the finding is not valid or
no change is required, do not modify code for that item and briefly explain why
it was skipped.
In `@components/google_analytics/sources/page-opened/page-opened.mjs` around lines
80 - 82, The fallback using report?.totals is dead because the runReport request
doesn't include metricAggregations; update the runReport request construction
(the object passed to analyticsDataClient.runReport) to include
metricAggregations: ["TOTAL"] so report.totals will be populated, and then keep
the pageViews computation (report?.rows?.. || report?.totals?.. || "0") as-is;
alternatively, if you prefer removing dead code, delete the report?.totals
fallback from the pageViews assignment that reads from report to rely solely on
report?.rows and "0".

@luancazarine luancazarine moved this from Changes Required to In Review in Component (Source and Action) Backlog Feb 18, 2026
Copy link
Copy Markdown
Collaborator

@luancazarine luancazarine left a comment

Choose a reason for hiding this comment

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

Hi @maycon-pipedream, LGTM! Ready for QA!

@luancazarine luancazarine moved this from In Review to Ready for QA in Component (Source and Action) Backlog Feb 18, 2026
@pipedream-component-development
Copy link
Copy Markdown
Collaborator

@luancazarine
Copy link
Copy Markdown
Collaborator

Hi @maycon-pipedream these are some QA tests at the moment:

  • this means the test ran succesfully
  • this means the test has some errors

Sources

page-opened

  • deploy
  • required props
  • optional props

@luancazarine luancazarine moved this from Ready for QA to In QA in Component (Source and Action) Backlog Feb 18, 2026
@luancazarine luancazarine moved this from In QA to Ready for Release in Component (Source and Action) Backlog Feb 18, 2026
@pipedream-component-development
Copy link
Copy Markdown
Collaborator

✅ All tests passed - ready for release!
📖 See the test report(s) below:

@luancazarine luancazarine merged commit f08960d into master Feb 18, 2026
9 checks passed
@luancazarine luancazarine deleted the fix/google_analytics branch February 18, 2026 23:16
@github-project-automation github-project-automation bot moved this from Ready for Release to Done in Component (Source and Action) Backlog Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants