From 18371f975f5b608caeb756cdb56c90180a2ac438 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:40:58 +0000 Subject: [PATCH 1/5] feat(dataconnect): Add GA metric for Cloud SQL setup Adds a new Google Analytics metric, `dataconnect_cloud_sql`, to track the usage and duration of the `setupCloudSql` function. This metric includes the following labels: - source: init, mcp_init, deploy - action: created, created_failed, updated, updated_failed, get - location - enable_google_ml_integration: true, false - result: error, success - free_trial: true, false - database_version: postgres_17 The implementation also tracks the duration of the `setupCloudSql` operation. To differentiate between `init` and `mcp_init` sources, an `isMcp` flag was added to the `Setup` object. --- src/dataconnect/provisionCloudSql.ts | 156 ++++++++++++++++++++----- src/deploy/dataconnect/deploy.ts | 1 + src/deploy/dataconnect/prepare.ts | 1 + src/init/features/dataconnect/index.ts | 1 + src/init/index.ts | 2 + src/track.ts | 1 + 6 files changed, 135 insertions(+), 27 deletions(-) diff --git a/src/dataconnect/provisionCloudSql.ts b/src/dataconnect/provisionCloudSql.ts index 4b5fbe4a045..902ad52c908 100755 --- a/src/dataconnect/provisionCloudSql.ts +++ b/src/dataconnect/provisionCloudSql.ts @@ -1,11 +1,13 @@ -import * as cloudSqlAdminClient from "../gcp/cloudsql/cloudsqladmin"; -import * as utils from "../utils"; import * as clc from "colorette"; -import { grantRolesToCloudSqlServiceAccount } from "./checkIam"; + +import * as cloudSqlAdminClient from "../gcp/cloudsql/cloudsqladmin"; import { Instance } from "../gcp/cloudsql/types"; -import { promiseWithSpinner } from "../utils"; import { logger } from "../logger"; -import { freeTrialTermsLink, checkFreeTrialInstanceUsed } from "./freeTrial"; +import { grantRolesToCloudSqlServiceAccount } from "./checkIam"; +import { checkFreeTrialInstanceUsed, freeTrialTermsLink } from "./freeTrial"; +import { promiseWithSpinner } from "../utils"; +import { trackGA4 } from "../track"; +import * as utils from "../utils"; const GOOGLE_ML_INTEGRATION_ROLE = "roles/aiplatform.user"; @@ -16,9 +18,11 @@ export async function setupCloudSql(args: { instanceId: string; databaseId: string; requireGoogleMlIntegration: boolean; + source: "init" | "mcp_init" | "deploy"; dryRun?: boolean; }): Promise { - await upsertInstance({ ...args }); + const startTime = Date.now(); + await upsertInstance({ ...args, startTime }); const { projectId, instanceId, requireGoogleMlIntegration, dryRun } = args; if (requireGoogleMlIntegration && !dryRun) { await grantRolesToCloudSqlServiceAccount(projectId, instanceId, [GOOGLE_ML_INTEGRATION_ROLE]); @@ -31,15 +35,32 @@ async function upsertInstance(args: { instanceId: string; databaseId: string; requireGoogleMlIntegration: boolean; + source: "init" | "mcp_init" | "deploy"; dryRun?: boolean; + startTime: number; }): Promise { - const { projectId, instanceId, requireGoogleMlIntegration, dryRun } = args; + const { projectId, instanceId, requireGoogleMlIntegration, dryRun, source, location, startTime } = + args; try { const existingInstance = await cloudSqlAdminClient.getInstance(projectId, instanceId); utils.logLabeledBullet( "dataconnect", `Found existing Cloud SQL instance ${clc.bold(instanceId)}.`, ); + const duration = Date.now() - startTime; + void trackGA4( + "dataconnect_cloud_sql", + { + source: source, + action: "get", + location: location, + enable_google_ml_integration: requireGoogleMlIntegration.toString(), + result: "success", + free_trial: "false", // existing instance can't be a new free trial one + database_version: "postgres_17", + }, + duration, + ); const why = getUpdateReason(existingInstance, requireGoogleMlIntegration); if (why) { if (dryRun) { @@ -55,19 +76,65 @@ async function upsertInstance(args: { `Cloud SQL instance ${clc.bold(instanceId)} settings are not compatible with Firebase Data Connect. ` + why, ); - await promiseWithSpinner( - () => - cloudSqlAdminClient.updateInstanceForDataConnect( - existingInstance, - requireGoogleMlIntegration, - ), - "Updating your Cloud SQL instance...", - ); + try { + await promiseWithSpinner( + () => + cloudSqlAdminClient.updateInstanceForDataConnect( + existingInstance, + requireGoogleMlIntegration, + ), + "Updating your Cloud SQL instance...", + ); + const duration = Date.now() - startTime; + void trackGA4( + "dataconnect_cloud_sql", + { + source: source, + action: "updated", + location: location, + enable_google_ml_integration: requireGoogleMlIntegration.toString(), + result: "success", + free_trial: "false", + database_version: "postgres_17", + }, + duration, + ); + } catch (err) { + const duration = Date.now() - startTime; + void trackGA4( + "dataconnect_cloud_sql", + { + source: source, + action: "updated_failed", + location: location, + enable_google_ml_integration: requireGoogleMlIntegration.toString(), + result: "error", + free_trial: "false", + database_version: "postgres_17", + }, + duration, + ); + throw err; + } } } await upsertDatabase({ ...args }); } catch (err: any) { if (err.status !== 404) { + const duration = Date.now() - startTime; + void trackGA4( + "dataconnect_cloud_sql", + { + source: source, + action: "get", + location: location, + enable_google_ml_integration: requireGoogleMlIntegration.toString(), + result: "error", + free_trial: "false", + database_version: "postgres_17", + }, + duration, + ); throw err; } // Cloud SQL instance is not found, start its creation. @@ -80,9 +147,12 @@ async function createInstance(args: { location: string; instanceId: string; requireGoogleMlIntegration: boolean; + source: "init" | "mcp_init" | "deploy"; dryRun?: boolean; + startTime: number; }): Promise { - const { projectId, location, instanceId, requireGoogleMlIntegration, dryRun } = args; + const { projectId, location, instanceId, requireGoogleMlIntegration, dryRun, source, startTime } = + args; const freeTrialUsed = await checkFreeTrialInstanceUsed(projectId); if (dryRun) { utils.logLabeledBullet( @@ -90,17 +160,49 @@ async function createInstance(args: { `Cloud SQL Instance ${clc.bold(instanceId)} not found. It will be created on your next deploy.`, ); } else { - await cloudSqlAdminClient.createInstance({ - projectId, - location, - instanceId, - enableGoogleMlIntegration: requireGoogleMlIntegration, - freeTrial: !freeTrialUsed, - }); - utils.logLabeledBullet( - "dataconnect", - cloudSQLBeingCreated(projectId, instanceId, !freeTrialUsed), - ); + try { + await cloudSqlAdminClient.createInstance({ + projectId, + location, + instanceId, + enableGoogleMlIntegration: requireGoogleMlIntegration, + freeTrial: !freeTrialUsed, + }); + const duration = Date.now() - startTime; + void trackGA4( + "dataconnect_cloud_sql", + { + source: source, + action: "created", + location: location, + enable_google_ml_integration: requireGoogleMlIntegration.toString(), + result: "success", + free_trial: (!freeTrialUsed).toString(), + database_version: "postgres_17", + }, + duration, + ); + utils.logLabeledBullet( + "dataconnect", + cloudSQLBeingCreated(projectId, instanceId, !freeTrialUsed), + ); + } catch (err) { + const duration = Date.now() - startTime; + void trackGA4( + "dataconnect_cloud_sql", + { + source: source, + action: "created_failed", + location: location, + enable_google_ml_integration: requireGoogleMlIntegration.toString(), + result: "error", + free_trial: (!freeTrialUsed).toString(), + database_version: "postgres_17", + }, + duration, + ); + throw err; + } } } diff --git a/src/deploy/dataconnect/deploy.ts b/src/deploy/dataconnect/deploy.ts index 6cf5f4cc64b..91cafd11ac1 100644 --- a/src/deploy/dataconnect/deploy.ts +++ b/src/deploy/dataconnect/deploy.ts @@ -96,6 +96,7 @@ export default async function ( instanceId, databaseId, requireGoogleMlIntegration: requiresVector(s.deploymentMetadata), + source: "deploy", }); } }), diff --git a/src/deploy/dataconnect/prepare.ts b/src/deploy/dataconnect/prepare.ts index 869426fcd3a..63fa87cf3ab 100644 --- a/src/deploy/dataconnect/prepare.ts +++ b/src/deploy/dataconnect/prepare.ts @@ -88,6 +88,7 @@ export default async function (context: any, options: DeployOptions): Promise; } @@ -149,6 +150,7 @@ export async function init(setup: Setup, config: Config, options: any): Promise< /** Actuate the feature init flow from firebase_init MCP tool. */ export async function actuate(setup: Setup, config: Config, options: any): Promise { + setup.isMcp = true; const nextFeature = setup.features?.shift(); if (nextFeature) { const start = process.uptime(); diff --git a/src/track.ts b/src/track.ts index e521ca11af2..38013f68be9 100644 --- a/src/track.ts +++ b/src/track.ts @@ -24,6 +24,7 @@ type cliEventNames = | "product_init" | "product_init_mcp" | "dataconnect_init" + | "dataconnect_cloud_sql" | "error" | "login" | "api_enabled" From f4ad6a58ec798701266adcf26491e01d836579bf Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Tue, 14 Oct 2025 10:31:57 -0700 Subject: [PATCH 2/5] ready --- src/dataconnect/provisionCloudSql.ts | 208 +++++++++---------------- src/gcp/cloudsql/cloudsqladmin.spec.ts | 4 +- src/gcp/cloudsql/cloudsqladmin.ts | 9 +- src/init/features/dataconnect/index.ts | 2 +- src/init/index.ts | 2 - 5 files changed, 85 insertions(+), 140 deletions(-) diff --git a/src/dataconnect/provisionCloudSql.ts b/src/dataconnect/provisionCloudSql.ts index 902ad52c908..da31a1cfa77 100755 --- a/src/dataconnect/provisionCloudSql.ts +++ b/src/dataconnect/provisionCloudSql.ts @@ -11,6 +11,12 @@ import * as utils from "../utils"; const GOOGLE_ML_INTEGRATION_ROLE = "roles/aiplatform.user"; +type SetupStats = { + action: "get" | "update" | "create"; + freeTrialLabel?: cloudSqlAdminClient.DataConnectLabel; + databaseVersion?: string; +}; + /** Sets up a Cloud SQL instance, database and its permissions. */ export async function setupCloudSql(args: { projectId: string; @@ -21,46 +27,60 @@ export async function setupCloudSql(args: { source: "init" | "mcp_init" | "deploy"; dryRun?: boolean; }): Promise { - const startTime = Date.now(); - await upsertInstance({ ...args, startTime }); const { projectId, instanceId, requireGoogleMlIntegration, dryRun } = args; + + const startTime = Date.now(); + const stats: SetupStats = { action: "get" }; + let success = false; + try { + await upsertInstance(stats, { ...args }); + success = true; + } finally { + if (!dryRun) { + await trackGA4( + "dataconnect_cloud_sql", + { + source: args.source, + action: success ? stats.action : `${stats.action}_error`, + location: args.location, + enable_google_ml_integration: args.requireGoogleMlIntegration.toString(), + database_version: stats.databaseVersion?.toLowerCase() || "unknown", + free_trial_label: stats.freeTrialLabel || "unknown", + }, + Date.now() - startTime, + ); + } + } + if (requireGoogleMlIntegration && !dryRun) { await grantRolesToCloudSqlServiceAccount(projectId, instanceId, [GOOGLE_ML_INTEGRATION_ROLE]); } } -async function upsertInstance(args: { - projectId: string; - location: string; - instanceId: string; - databaseId: string; - requireGoogleMlIntegration: boolean; - source: "init" | "mcp_init" | "deploy"; - dryRun?: boolean; - startTime: number; -}): Promise { - const { projectId, instanceId, requireGoogleMlIntegration, dryRun, source, location, startTime } = - args; +async function upsertInstance( + stats: SetupStats, + args: { + projectId: string; + location: string; + instanceId: string; + databaseId: string; + requireGoogleMlIntegration: boolean; + dryRun?: boolean; + }, +): Promise { + const { projectId, instanceId, requireGoogleMlIntegration, dryRun } = args; try { const existingInstance = await cloudSqlAdminClient.getInstance(projectId, instanceId); utils.logLabeledBullet( "dataconnect", `Found existing Cloud SQL instance ${clc.bold(instanceId)}.`, ); - const duration = Date.now() - startTime; - void trackGA4( - "dataconnect_cloud_sql", - { - source: source, - action: "get", - location: location, - enable_google_ml_integration: requireGoogleMlIntegration.toString(), - result: "success", - free_trial: "false", // existing instance can't be a new free trial one - database_version: "postgres_17", - }, - duration, - ); + const label = existingInstance.settings?.userLabels?.["firebase-data-connect"]; + if (label === "ft" || label === "nt") { + stats.freeTrialLabel = label; + } + stats.databaseVersion = existingInstance.databaseVersion; + const why = getUpdateReason(existingInstance, requireGoogleMlIntegration); if (why) { if (dryRun) { @@ -76,69 +96,28 @@ async function upsertInstance(args: { `Cloud SQL instance ${clc.bold(instanceId)} settings are not compatible with Firebase Data Connect. ` + why, ); - try { - await promiseWithSpinner( - () => - cloudSqlAdminClient.updateInstanceForDataConnect( - existingInstance, - requireGoogleMlIntegration, - ), - "Updating your Cloud SQL instance...", - ); - const duration = Date.now() - startTime; - void trackGA4( - "dataconnect_cloud_sql", - { - source: source, - action: "updated", - location: location, - enable_google_ml_integration: requireGoogleMlIntegration.toString(), - result: "success", - free_trial: "false", - database_version: "postgres_17", - }, - duration, - ); - } catch (err) { - const duration = Date.now() - startTime; - void trackGA4( - "dataconnect_cloud_sql", - { - source: source, - action: "updated_failed", - location: location, - enable_google_ml_integration: requireGoogleMlIntegration.toString(), - result: "error", - free_trial: "false", - database_version: "postgres_17", - }, - duration, - ); - throw err; - } + stats.action = "update"; + await promiseWithSpinner( + () => + cloudSqlAdminClient.updateInstanceForDataConnect( + existingInstance, + requireGoogleMlIntegration, + ), + "Updating your Cloud SQL instance...", + ); } } await upsertDatabase({ ...args }); } catch (err: any) { if (err.status !== 404) { - const duration = Date.now() - startTime; - void trackGA4( - "dataconnect_cloud_sql", - { - source: source, - action: "get", - location: location, - enable_google_ml_integration: requireGoogleMlIntegration.toString(), - result: "error", - free_trial: "false", - database_version: "postgres_17", - }, - duration, - ); throw err; } // Cloud SQL instance is not found, start its creation. - await createInstance({ ...args }); + stats.action = "create"; + const freeTrialUsed = await checkFreeTrialInstanceUsed(projectId); + stats.freeTrialLabel = freeTrialUsed ? "nt" : "ft"; + stats.databaseVersion = cloudSqlAdminClient.DEFAULT_DATABASE_VERSION; + await createInstance({ ...args, freeTrialLabel: stats.freeTrialLabel }); } } @@ -147,62 +126,28 @@ async function createInstance(args: { location: string; instanceId: string; requireGoogleMlIntegration: boolean; - source: "init" | "mcp_init" | "deploy"; + freeTrialLabel: cloudSqlAdminClient.DataConnectLabel; dryRun?: boolean; - startTime: number; }): Promise { - const { projectId, location, instanceId, requireGoogleMlIntegration, dryRun, source, startTime } = + const { projectId, location, instanceId, requireGoogleMlIntegration, dryRun, freeTrialLabel } = args; - const freeTrialUsed = await checkFreeTrialInstanceUsed(projectId); if (dryRun) { utils.logLabeledBullet( "dataconnect", `Cloud SQL Instance ${clc.bold(instanceId)} not found. It will be created on your next deploy.`, ); } else { - try { - await cloudSqlAdminClient.createInstance({ - projectId, - location, - instanceId, - enableGoogleMlIntegration: requireGoogleMlIntegration, - freeTrial: !freeTrialUsed, - }); - const duration = Date.now() - startTime; - void trackGA4( - "dataconnect_cloud_sql", - { - source: source, - action: "created", - location: location, - enable_google_ml_integration: requireGoogleMlIntegration.toString(), - result: "success", - free_trial: (!freeTrialUsed).toString(), - database_version: "postgres_17", - }, - duration, - ); - utils.logLabeledBullet( - "dataconnect", - cloudSQLBeingCreated(projectId, instanceId, !freeTrialUsed), - ); - } catch (err) { - const duration = Date.now() - startTime; - void trackGA4( - "dataconnect_cloud_sql", - { - source: source, - action: "created_failed", - location: location, - enable_google_ml_integration: requireGoogleMlIntegration.toString(), - result: "error", - free_trial: (!freeTrialUsed).toString(), - database_version: "postgres_17", - }, - duration, - ); - throw err; - } + await cloudSqlAdminClient.createInstance({ + projectId, + location, + instanceId, + enableGoogleMlIntegration: requireGoogleMlIntegration, + freeTrialLabel, + }); + utils.logLabeledBullet( + "dataconnect", + cloudSQLBeingCreated(projectId, instanceId, freeTrialLabel === "ft"), + ); } } @@ -266,8 +211,7 @@ export function getUpdateReason(instance: Instance, requireGoogleMlIntegration: if (!settings.ipConfiguration?.ipv4Enabled) { utils.logLabeledWarning( "dataconnect", - `Cloud SQL instance ${clc.bold(instance.name)} does not have a public IP. - ${clc.bold("firebase dataconnect:sql:migrate")} will only work within its VPC (e.g. GCE, GKE).`, + `Cloud SQL instance ${clc.bold(instance.name)} does not have a public IP.\n ${clc.bold("firebase dataconnect:sql:migrate")} will only work within its VPC (e.g. GCE, GKE).`, ); if ( settings.ipConfiguration?.privateNetwork && diff --git a/src/gcp/cloudsql/cloudsqladmin.spec.ts b/src/gcp/cloudsql/cloudsqladmin.spec.ts index b559b055697..258b5b4ae5d 100644 --- a/src/gcp/cloudsql/cloudsqladmin.spec.ts +++ b/src/gcp/cloudsql/cloudsqladmin.spec.ts @@ -93,7 +93,7 @@ describe("cloudsqladmin", () => { location: "us-central", instanceId: INSTANCE_ID, enableGoogleMlIntegration: false, - freeTrial: false, + freeTrialLabel: "nt", }), ).to.be.rejectedWith("Cloud SQL free trial instances are not yet available in us-central"); expect(nock.isDone()).to.be.true; @@ -160,7 +160,7 @@ describe("cloudsqladmin", () => { location: "us-central", instanceId: INSTANCE_ID, enableGoogleMlIntegration: false, - freeTrial: false, + freeTrialLabel: "nt", }); expect(nock.isDone()).to.be.true; diff --git a/src/gcp/cloudsql/cloudsqladmin.ts b/src/gcp/cloudsql/cloudsqladmin.ts index 8a7e1e5e97c..39340255336 100755 --- a/src/gcp/cloudsql/cloudsqladmin.ts +++ b/src/gcp/cloudsql/cloudsqladmin.ts @@ -59,12 +59,15 @@ export function instanceConsoleLink(projectId: string, instanceId: string) { return `https://console.cloud.google.com/sql/instances/${instanceId}/overview?project=${projectId}`; } +export type DataConnectLabel = "ft" | "nt"; +export const DEFAULT_DATABASE_VERSION = "POSTGRES_15"; + export async function createInstance(args: { projectId: string; location: string; instanceId: string; enableGoogleMlIntegration: boolean; - freeTrial: boolean; + freeTrialLabel: DataConnectLabel; }): Promise { const databaseFlags = [{ name: "cloudsql.iam_authentication", value: "on" }]; if (args.enableGoogleMlIntegration) { @@ -74,7 +77,7 @@ export async function createInstance(args: { await client.post, Operation>(`projects/${args.projectId}/instances`, { name: args.instanceId, region: args.location, - databaseVersion: "POSTGRES_15", + databaseVersion: DEFAULT_DATABASE_VERSION, settings: { tier: "db-f1-micro", edition: "ENTERPRISE", @@ -84,7 +87,7 @@ export async function createInstance(args: { enableGoogleMlIntegration: args.enableGoogleMlIntegration, databaseFlags, storageAutoResize: false, - userLabels: { "firebase-data-connect": args.freeTrial ? "ft" : "nt" }, + userLabels: { "firebase-data-connect": args.freeTrialLabel }, insightsConfig: { queryInsightsEnabled: true, queryPlansPerMinute: 5, // Match the default settings diff --git a/src/init/features/dataconnect/index.ts b/src/init/features/dataconnect/index.ts index 39574523829..7171ce4b3b1 100644 --- a/src/init/features/dataconnect/index.ts +++ b/src/init/features/dataconnect/index.ts @@ -220,7 +220,7 @@ async function actuateWithInfo( instanceId: info.cloudSqlInstanceId, databaseId: info.cloudSqlDatabase, requireGoogleMlIntegration: false, - source: setup.isMcp ? "mcp_init" : "init", + source: info.analyticsFlow.startsWith("mcp") ? "mcp_init" : "init", }); } diff --git a/src/init/index.ts b/src/init/index.ts index f152f413b3d..f3ca925b9dc 100644 --- a/src/init/index.ts +++ b/src/init/index.ts @@ -27,7 +27,6 @@ export interface Setup { projectId?: string; projectLocation?: string; isBillingEnabled?: boolean; - isMcp?: boolean; hosting?: Record; } @@ -150,7 +149,6 @@ export async function init(setup: Setup, config: Config, options: any): Promise< /** Actuate the feature init flow from firebase_init MCP tool. */ export async function actuate(setup: Setup, config: Config, options: any): Promise { - setup.isMcp = true; const nextFeature = setup.features?.shift(); if (nextFeature) { const start = process.uptime(); From cb614db8858b4669010030352004dbf20016d71d Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Tue, 14 Oct 2025 10:48:14 -0700 Subject: [PATCH 3/5] clean --- src/dataconnect/provisionCloudSql.ts | 20 ++++++++++---------- src/init/features/dataconnect/index.ts | 2 +- src/init/features/dataconnect/sdk.ts | 4 ++-- src/track.ts | 1 + 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/dataconnect/provisionCloudSql.ts b/src/dataconnect/provisionCloudSql.ts index da31a1cfa77..ff8bd33ccef 100755 --- a/src/dataconnect/provisionCloudSql.ts +++ b/src/dataconnect/provisionCloudSql.ts @@ -13,8 +13,8 @@ const GOOGLE_ML_INTEGRATION_ROLE = "roles/aiplatform.user"; type SetupStats = { action: "get" | "update" | "create"; - freeTrialLabel?: cloudSqlAdminClient.DataConnectLabel; databaseVersion?: string; + dataconnectLabel?: cloudSqlAdminClient.DataConnectLabel; }; /** Sets up a Cloud SQL instance, database and its permissions. */ @@ -45,7 +45,7 @@ export async function setupCloudSql(args: { location: args.location, enable_google_ml_integration: args.requireGoogleMlIntegration.toString(), database_version: stats.databaseVersion?.toLowerCase() || "unknown", - free_trial_label: stats.freeTrialLabel || "unknown", + free_trial_label: stats.dataconnectLabel || "unknown", }, Date.now() - startTime, ); @@ -75,11 +75,10 @@ async function upsertInstance( "dataconnect", `Found existing Cloud SQL instance ${clc.bold(instanceId)}.`, ); - const label = existingInstance.settings?.userLabels?.["firebase-data-connect"]; - if (label === "ft" || label === "nt") { - stats.freeTrialLabel = label; - } stats.databaseVersion = existingInstance.databaseVersion; + stats.dataconnectLabel = existingInstance.settings?.userLabels?.["firebase-data-connect"] as + | cloudSqlAdminClient.DataConnectLabel + | undefined; const why = getUpdateReason(existingInstance, requireGoogleMlIntegration); if (why) { @@ -114,10 +113,10 @@ async function upsertInstance( } // Cloud SQL instance is not found, start its creation. stats.action = "create"; - const freeTrialUsed = await checkFreeTrialInstanceUsed(projectId); - stats.freeTrialLabel = freeTrialUsed ? "nt" : "ft"; stats.databaseVersion = cloudSqlAdminClient.DEFAULT_DATABASE_VERSION; - await createInstance({ ...args, freeTrialLabel: stats.freeTrialLabel }); + const freeTrialUsed = await checkFreeTrialInstanceUsed(projectId); + stats.dataconnectLabel = freeTrialUsed ? "nt" : "ft"; + await createInstance({ ...args, freeTrialLabel: stats.dataconnectLabel }); } } @@ -211,7 +210,8 @@ export function getUpdateReason(instance: Instance, requireGoogleMlIntegration: if (!settings.ipConfiguration?.ipv4Enabled) { utils.logLabeledWarning( "dataconnect", - `Cloud SQL instance ${clc.bold(instance.name)} does not have a public IP.\n ${clc.bold("firebase dataconnect:sql:migrate")} will only work within its VPC (e.g. GCE, GKE).`, + `Cloud SQL instance ${clc.bold(instance.name)} does not have a public IP. + ${clc.bold("firebase dataconnect:sql:migrate")} will only work within its VPC (e.g. GCE, GKE).`, ); if ( settings.ipConfiguration?.privateNetwork && diff --git a/src/init/features/dataconnect/index.ts b/src/init/features/dataconnect/index.ts index 7171ce4b3b1..2abcf0c7d89 100644 --- a/src/init/features/dataconnect/index.ts +++ b/src/init/features/dataconnect/index.ts @@ -170,7 +170,7 @@ export async function actuate(setup: Setup, config: Config, options: any): Promi await actuateWithInfo(setup, config, info, options); await sdk.actuate(setup, config); } finally { - void trackGA4("dataconnect_init", { + await trackGA4("dataconnect_init", { flow: info.analyticsFlow, project_status: setup.projectId ? setup.isBillingEnabled diff --git a/src/init/features/dataconnect/sdk.ts b/src/init/features/dataconnect/sdk.ts index 8bc6ab36419..309a5f403be 100644 --- a/src/init/features/dataconnect/sdk.ts +++ b/src/init/features/dataconnect/sdk.ts @@ -165,13 +165,13 @@ export async function actuate(setup: Setup, config: Config) { } finally { let flow = "no_app"; if (sdkInfo.apps.length) { - const platforms = sdkInfo.apps.map(appDescription).sort(); + const platforms = sdkInfo.apps.map((a) => a.platform.toLowerCase()).sort(); flow = `${platforms.join("_")}_app`; } if (fdcInfo) { fdcInfo.analyticsFlow += `_${flow}`; } else { - void trackGA4("dataconnect_init", { + await trackGA4("dataconnect_init", { project_status: setup.projectId ? (setup.isBillingEnabled ? "blaze" : "spark") : "missing", flow: `cli_sdk_${flow}`, }); diff --git a/src/track.ts b/src/track.ts index 38013f68be9..d0974361282 100644 --- a/src/track.ts +++ b/src/track.ts @@ -139,6 +139,7 @@ export async function trackGA4( params: AnalyticsParams, duration: number = 1, // Default to 1ms duration so that events show up in realtime view. ): Promise { + console.log("stats", eventName, params); const session = cliSession(); if (!session) { return; From cda80ce661c30a034ceb4b47deb7e32ee4cf7e4f Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Tue, 14 Oct 2025 10:56:11 -0700 Subject: [PATCH 4/5] name --- src/dataconnect/provisionCloudSql.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dataconnect/provisionCloudSql.ts b/src/dataconnect/provisionCloudSql.ts index ff8bd33ccef..0b22170d61f 100755 --- a/src/dataconnect/provisionCloudSql.ts +++ b/src/dataconnect/provisionCloudSql.ts @@ -45,7 +45,7 @@ export async function setupCloudSql(args: { location: args.location, enable_google_ml_integration: args.requireGoogleMlIntegration.toString(), database_version: stats.databaseVersion?.toLowerCase() || "unknown", - free_trial_label: stats.dataconnectLabel || "unknown", + dataconnect_label: stats.dataconnectLabel || "unknown", }, Date.now() - startTime, ); From af27115ce626737f3d48c99dacf6b275a0ef3390 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Tue, 14 Oct 2025 12:04:30 -0700 Subject: [PATCH 5/5] nits --- src/init/features/dataconnect/index.ts | 2 +- src/init/features/dataconnect/sdk.ts | 2 +- src/track.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/init/features/dataconnect/index.ts b/src/init/features/dataconnect/index.ts index 2abcf0c7d89..7171ce4b3b1 100644 --- a/src/init/features/dataconnect/index.ts +++ b/src/init/features/dataconnect/index.ts @@ -170,7 +170,7 @@ export async function actuate(setup: Setup, config: Config, options: any): Promi await actuateWithInfo(setup, config, info, options); await sdk.actuate(setup, config); } finally { - await trackGA4("dataconnect_init", { + void trackGA4("dataconnect_init", { flow: info.analyticsFlow, project_status: setup.projectId ? setup.isBillingEnabled diff --git a/src/init/features/dataconnect/sdk.ts b/src/init/features/dataconnect/sdk.ts index 309a5f403be..d5a9eb5b58c 100644 --- a/src/init/features/dataconnect/sdk.ts +++ b/src/init/features/dataconnect/sdk.ts @@ -171,7 +171,7 @@ export async function actuate(setup: Setup, config: Config) { if (fdcInfo) { fdcInfo.analyticsFlow += `_${flow}`; } else { - await trackGA4("dataconnect_init", { + void trackGA4("dataconnect_init", { project_status: setup.projectId ? (setup.isBillingEnabled ? "blaze" : "spark") : "missing", flow: `cli_sdk_${flow}`, }); diff --git a/src/track.ts b/src/track.ts index d0974361282..38013f68be9 100644 --- a/src/track.ts +++ b/src/track.ts @@ -139,7 +139,6 @@ export async function trackGA4( params: AnalyticsParams, duration: number = 1, // Default to 1ms duration so that events show up in realtime view. ): Promise { - console.log("stats", eventName, params); const session = cliSession(); if (!session) { return;