From ed02cf15fad7a5003fd7c33f90a608af16670f5c Mon Sep 17 00:00:00 2001 From: Saatvik Arya Date: Sun, 23 Aug 2026 05:20:18 +0530 Subject: [PATCH] feat(openapi): add Google Analytics API presets Expose the GA4 Data and Analytics Admin Discovery APIs as built-in Google integrations while keeping their default OAuth grant read-only. --- .../google/__snapshots__/presets.test.ts.snap | 8 +++ .../src/providers/google/presets.test.ts | 66 ++++++++++++++++++- .../openapi/src/providers/google/presets.ts | 17 +++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/packages/plugins/openapi/src/providers/google/__snapshots__/presets.test.ts.snap b/packages/plugins/openapi/src/providers/google/__snapshots__/presets.test.ts.snap index f750036024..b7c1075920 100644 --- a/packages/plugins/openapi/src/providers/google/__snapshots__/presets.test.ts.snap +++ b/packages/plugins/openapi/src/providers/google/__snapshots__/presets.test.ts.snap @@ -62,6 +62,14 @@ exports[`classifies every Google service for bundle OAuth UX 1`] = ` "id": "google-search-console", "oauthAudience": "standard-user", }, + { + "id": "google-analytics-data", + "oauthAudience": "standard-user", + }, + { + "id": "google-analytics-admin", + "oauthAudience": "standard-user", + }, { "id": "google-classroom", "oauthAudience": "advanced-user", diff --git a/packages/plugins/openapi/src/providers/google/presets.test.ts b/packages/plugins/openapi/src/providers/google/presets.test.ts index fcd33712b0..8fe7f24b40 100644 --- a/packages/plugins/openapi/src/providers/google/presets.test.ts +++ b/packages/plugins/openapi/src/providers/google/presets.test.ts @@ -3,7 +3,12 @@ import { Effect } from "effect"; import { compileOpenApiSpec } from "@executor-js/plugin-openapi"; import { convertGoogleDiscoveryBundleToOpenApi } from "./discovery"; -import { googleCatalog, googleOpenApiPresets, googleStandardUserOAuthPresets } from "./presets"; +import { + googleCatalog, + googleCatalogOAuthScopesForPreset, + googleOpenApiPresets, + googleStandardUserOAuthPresets, +} from "./presets"; const googleHealthCheckDiscoveryFixtures = { "google-calendar": { @@ -170,6 +175,46 @@ const googleHealthCheckDiscoveryFixtures = { }, }, }, + "google-analytics-admin": { + url: "https://analyticsadmin.googleapis.com/$discovery/rest?version=v1beta", + document: { + name: "analyticsadmin", + version: "v1beta", + title: "Google Analytics Admin API", + rootUrl: "https://analyticsadmin.googleapis.com/", + servicePath: "", + resources: { + accountSummaries: { + methods: { + list: { + id: "analyticsadmin.accountSummaries.list", + path: "v1beta/accountSummaries", + httpMethod: "GET", + response: { $ref: "GoogleAnalyticsAdminV1betaListAccountSummariesResponse" }, + }, + }, + }, + }, + schemas: { + GoogleAnalyticsAdminV1betaListAccountSummariesResponse: { + type: "object", + properties: { + accountSummaries: { + type: "array", + items: { $ref: "GoogleAnalyticsAdminV1betaAccountSummary" }, + }, + }, + }, + GoogleAnalyticsAdminV1betaAccountSummary: { + type: "object", + properties: { + account: { type: "string" }, + displayName: { type: "string" }, + }, + }, + }, + }, + }, } as const; const FROZEN_GOOGLE_SLUGS = [ @@ -188,6 +233,8 @@ const FROZEN_GOOGLE_SLUGS = [ "google_keep", "google_youtube_data", "google_search_console", + "google_analytics_data", + "google_analytics_admin", "google_classroom", "google_admin_directory", "google_admin_reports", @@ -204,6 +251,8 @@ it("keeps Select all limited to Google services that can use normal user OAuth", expect(standardIds).toContain("google-tasks"); expect(standardIds).toContain("google-people"); expect(standardIds).toContain("google-search-console"); + expect(standardIds).toContain("google-analytics-data"); + expect(standardIds).toContain("google-analytics-admin"); expect(standardIds).not.toContain("google-youtube-data"); expect(standardIds).not.toContain("google-cloud-resource-manager"); @@ -213,6 +262,20 @@ it("keeps Select all limited to Google services that can use normal user OAuth", expect(standardIds).not.toContain("google-admin-reports"); }); +it("uses read-only OAuth for both Google Analytics APIs", () => { + const readOnlyScope = "https://www.googleapis.com/auth/analytics.readonly"; + const writeScopes = [ + "https://www.googleapis.com/auth/analytics", + "https://www.googleapis.com/auth/analytics.edit", + ]; + + for (const presetId of ["google-analytics-data", "google-analytics-admin"]) { + const scopes = googleCatalogOAuthScopesForPreset(presetId); + expect(scopes).toContain(readOnlyScope); + for (const writeScope of writeScopes) expect(scopes).not.toContain(writeScope); + } +}); + it("classifies every Google service for bundle OAuth UX", () => { expect( googleOpenApiPresets.map((preset) => ({ @@ -273,6 +336,7 @@ it("omits Google health checks when the service spec has no stable cheap read", "google-slides", "google-forms", "google-photos-picker", + "google-analytics-data", "google-admin-reports", ]; diff --git a/packages/plugins/openapi/src/providers/google/presets.ts b/packages/plugins/openapi/src/providers/google/presets.ts index 8b0ff9336f..1596cb8878 100644 --- a/packages/plugins/openapi/src/providers/google/presets.ts +++ b/packages/plugins/openapi/src/providers/google/presets.ts @@ -152,6 +152,20 @@ export const googleOpenApiPresets: readonly GoogleOpenApiPreset[] = [ url: gd("searchconsole", "v1"), oauthAudience: "standard-user", }, + { + id: "google-analytics-data", + name: "Google Analytics Data", + summary: "Reports, realtime metrics, funnels, and audience exports for GA4 properties.", + url: "https://analyticsdata.googleapis.com/$discovery/rest?version=v1beta", + oauthAudience: "standard-user", + }, + { + id: "google-analytics-admin", + name: "Google Analytics Admin", + summary: "Accounts, properties, data streams, key events, and analytics configuration.", + url: "https://analyticsadmin.googleapis.com/$discovery/rest?version=v1beta", + oauthAudience: "standard-user", + }, { id: "google-classroom", name: "Google Classroom", @@ -241,6 +255,8 @@ export const googleOAuthConsentScopes: Readonly> = { args: { part: "id", mine: true }, }, "google-search-console": { operation: "webmasters.sites.list" }, + "google-analytics-admin": { operation: "analyticsadmin.accountSummaries.list" }, "google-classroom": { operation: "classroom.courses.list" }, "google-admin-directory": { operation: "directory.users.list",