[go: nahoru, domu]

Skip to content

Commit

Permalink
Track telemetry in vscode extension (#7238)
Browse files Browse the repository at this point in the history
* Add metrics

* add comment

* address comments
  • Loading branch information
hlshen committed May 31, 2024
1 parent 96fe35f commit ffd9502
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 59 deletions.
7 changes: 5 additions & 2 deletions firebase-vscode/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"version": "0.2.3",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "${defaultBuildTask}"
"preLaunchTask": "${defaultBuildTask}",
"env": {
"VSCODE_DEBUG_MODE": "true"
}
},
{
"name": "Extension Tests",
Expand Down
35 changes: 30 additions & 5 deletions firebase-vscode/src/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { env, TelemetryLogger, TelemetrySender } from "vscode";
import { pluginLogger } from "./logger-wrapper";
import { AnalyticsParams, trackVSCode } from "./track";
import { AnalyticsParams, trackVSCode } from "../../src/track";

export enum DATA_CONNECT_EVENT_NAME {
COMMAND_EXECUTION = "command_execution",
DEPLOY_ALL = "deploy_all",
DEPLOY_INDIVIDUAL = "deploy_individual",
IDX_LOGIN = "idx_login",
LOGIN = "login",
PROJECT_SELECT = "project_select",
RUN_LOCAL = "run_local",
RUN_PROD = "run_prod",
ADD_DATA = "add_data",
READ_DATA = "read_data",
MOVE_TO_CONNECTOR = "move_to_connector",
START_EMULATOR_FROM_EXECUTION = "start_emulator_from_execution",
REFUSE_START_EMULATOR_FROM_EXECUTION = "refuse_start_emulator_from_execution",
}

export class AnalyticsLogger {
readonly logger: TelemetryLogger;
Expand All @@ -12,19 +28,28 @@ export class AnalyticsLogger {
}

class GA4TelemetrySender implements TelemetrySender {
constructor(readonly pluginLogger) {}
constructor(
readonly pluginLogger,
) {}

sendEventData(
eventName: string,
data?: Record<string, any> | undefined,
): void {
if (!env.isTelemetryEnabled) {
pluginLogger.warn("Telemetry is not enabled.");
this.pluginLogger.warn("Telemetry is not enabled.");
return;
}
if (!data) {
return;

// telemetry logger adds prefixes to eventName and params that are disallowed in GA4
eventName = eventName.replace("firebase.firebase-vscode/", "");
for (const key in data) {
if (key.includes("common.")) {
data[key.replace("common.", "")] = data[key];
delete data[key];
}
}
data = { ...data };
trackVSCode(eventName, data as AnalyticsParams);
}

Expand Down
16 changes: 7 additions & 9 deletions firebase-vscode/src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import vscode, { Disposable, ExtensionContext } from "vscode";
import vscode, { Disposable, ExtensionContext, TelemetryLogger } from "vscode";
import { ExtensionBrokerImpl } from "../extension-broker";
import { registerConfig } from "./config";
import { EmulatorsController } from "./emulators";
Expand All @@ -11,13 +11,11 @@ import { registerProject } from "./project";
import { registerQuickstart } from "./quickstart";
import { registerOptions } from "../options";

export async function registerCore({
broker,
context,
}: {
broker: ExtensionBrokerImpl;
context: ExtensionContext;
}): Promise<[EmulatorsController, vscode.Disposable]> {
export async function registerCore(
broker: ExtensionBrokerImpl,
context: ExtensionContext,
telemetryLogger: TelemetryLogger,
): Promise<[EmulatorsController, vscode.Disposable]> {
const settings = getSettings();

if (settings.npmPath) {
Expand Down Expand Up @@ -60,7 +58,7 @@ export async function registerCore({
registerOptions(context),
registerConfig(broker),
registerEnv(broker),
registerUser(broker),
registerUser(broker, telemetryLogger),
registerProject(broker),
registerQuickstart(broker),
{ dispose: sub1 },
Expand Down
6 changes: 4 additions & 2 deletions firebase-vscode/src/core/user.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { computed, effect } from "@preact/signals-react";
import { Disposable } from "vscode";
import { Disposable, TelemetryLogger } from "vscode";
import { ServiceAccountUser } from "../types";
import { User as AuthUser } from "../../../src/types/auth";
import { ExtensionBrokerImpl } from "../extension-broker";
import { getAccounts, login, logoutUser } from "../cli";
import { globalSignal } from "../utils/globals";
import { DATA_CONNECT_EVENT_NAME } from "../analytics";

type User = ServiceAccountUser | AuthUser;

Expand All @@ -31,7 +32,7 @@ export async function checkLogin() {
);
}

export function registerUser(broker: ExtensionBrokerImpl): Disposable {
export function registerUser(broker: ExtensionBrokerImpl, telemetryLogger: TelemetryLogger): Disposable {

const sub1 = effect(() => {
broker.send("notifyUsers", { users: Object.values(users.value) });
Expand All @@ -46,6 +47,7 @@ export function registerUser(broker: ExtensionBrokerImpl): Disposable {
});

const sub4 = broker.on("addUser", async () => {
telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.LOGIN);
const { user } = await login();
users.value = {
...users.value,
Expand Down
15 changes: 11 additions & 4 deletions firebase-vscode/src/data-connect/ad-hoc-mutations.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import vscode, { Disposable } from "vscode";
import vscode, { Disposable, TelemetryLogger } from "vscode";
import { DocumentNode, GraphQLInputObjectType, GraphQLScalarType, Kind, ObjectTypeDefinitionNode, buildClientSchema, buildSchema } from "graphql";
import { checkIfFileExists, upsertFile } from "./file-utils";
import { DataConnectService } from "./service";
import { DATA_CONNECT_EVENT_NAME } from "../analytics";

export function registerAdHoc(dataConnectService: DataConnectService): Disposable {
export function registerAdHoc(dataConnectService: DataConnectService, telemetryLogger: TelemetryLogger): Disposable {
const defaultScalarValues = {
Any: "{}",
AuthUID: '""',
Expand Down Expand Up @@ -187,11 +188,17 @@ query {
return Disposable.from(
vscode.commands.registerCommand(
"firebase.dataConnect.schemaAddData",
schemaAddData,
(ast) => {
telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.ADD_DATA);
schemaAddData(ast);
},
),
vscode.commands.registerCommand(
"firebase.dataConnect.schemaReadData",
schemaReadData,
(document, ast) => {
telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.READ_DATA);
schemaReadData(document, ast);
},
),
);
}
44 changes: 25 additions & 19 deletions firebase-vscode/src/data-connect/connectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import vscode, {
ExtensionContext,
InputBoxValidationMessage,
InputBoxValidationSeverity,
TelemetryLogger,
} from "vscode";
import { ExtensionBrokerImpl } from "../extension-broker";
import {
Expand Down Expand Up @@ -38,16 +39,18 @@ import { DataConnectService } from "./service";
import { OperationLocation } from "./types";
import { checkIfFileExists } from "./file-utils";
import * as path from "path";
import { DATA_CONNECT_EVENT_NAME } from "../analytics";

export function registerConnectors(
context: ExtensionContext,
broker: ExtensionBrokerImpl,
dataConnectService: DataConnectService
dataConnectService: DataConnectService,
telemetryLogger: TelemetryLogger,
): Disposable {
async function moveOperationToConnector(
defIndex: number, // The index of the definition to move.
{ documentPath, document }: OperationLocation,
connectorPath: string
connectorPath: string,
) {
const ast = parse(new Source(document, documentPath));

Expand All @@ -61,7 +64,7 @@ export function registerConnectors(
const introspect = (await dataConnectService.introspect())?.data;
if (!introspect) {
vscode.window.showErrorMessage(
"Failed to introspect the types. (Is the emulator running?)"
"Failed to introspect the types. (Is the emulator running?)",
);
return;
}
Expand Down Expand Up @@ -98,7 +101,7 @@ export function registerConnectors(
},
};
},
})
}),
)[opName];
// opAst contains only the operation we care about plus fragments used.
if (!opAst) {
Expand Down Expand Up @@ -135,11 +138,11 @@ export function registerConnectors(
// TODO: Consider removing the operation from the original document?

vscode.window.showInformationMessage(
`Moved ${opName} to ${vscode.workspace.asRelativePath(filePath)}`
`Moved ${opName} to ${vscode.workspace.asRelativePath(filePath)}`,
);

async function validateOpName(
value: string
value: string,
): Promise<InputBoxValidationMessage | null> {
if (!value) {
return {
Expand Down Expand Up @@ -186,7 +189,7 @@ export function registerConnectors(

function findExtractCandidates(
ast: DocumentNode,
introspect: IntrospectionQuery
introspect: IntrospectionQuery,
): ExtractCandidate[] {
const candidates: ExtractCandidate[] = [];
const seenVarNames = new Set<string>();
Expand Down Expand Up @@ -238,7 +241,7 @@ export function registerConnectors(
if (argName) {
// This should be impossible to reach.
throw new Error(
`Found Argument within Argument: (${argName} > ${node.name.value}).`
`Found Argument within Argument: (${argName} > ${node.name.value}).`,
);
}
argName = node.name.value;
Expand All @@ -248,8 +251,8 @@ export function registerConnectors(
`Cannot resolve argument type for ${displayPath(
fieldPath,
directiveName,
argName
)}.`
argName,
)}.`,
);
}
if (addCandidate(node, arg.type)) {
Expand Down Expand Up @@ -289,13 +292,13 @@ export function registerConnectors(
return false;
},
},
})
}),
);
return candidates;

function addCandidate(
node: ObjectFieldNode | ArgumentNode,
type: GraphQLInputType
type: GraphQLInputType,
): boolean {
if (!isConstValueNode(node.value)) {
return false;
Expand All @@ -308,7 +311,7 @@ export function registerConnectors(
fieldPath,
directiveName,
argName,
valuePath
valuePath,
);
seenVarNames.add(varName);
candidates.push({
Expand All @@ -323,7 +326,7 @@ export function registerConnectors(
directiveName,
argName,
valuePath,
"$" + varName
"$" + varName,
),
// Typical enums such as OrderBy are unlikely to be made variables.
// Similarly, null literals aren't usually meant to be changed.
Expand All @@ -335,7 +338,7 @@ export function registerConnectors(

function extractVariables(
opAst: DocumentNode,
picked: ExtractCandidate[]
picked: ExtractCandidate[],
): DocumentNode {
const pickedByParent = new Map<ASTNode, ExtractCandidate>();
for (const p of picked) {
Expand Down Expand Up @@ -408,7 +411,7 @@ export function registerConnectors(
directiveName?: string,
argName?: string,
valuePath?: string[],
valueDisp = "<value>"
valueDisp = "<value>",
): string {
let fieldDisp = fieldPath.join(".");
if (directiveName) {
Expand All @@ -434,7 +437,7 @@ export function registerConnectors(
fieldPath: string[],
directiveName?: string,
argName?: string,
valuePath?: string[]
valuePath?: string[],
): string {
const path = [...fieldPath];
if (argName) {
Expand Down Expand Up @@ -471,8 +474,11 @@ export function registerConnectors(
return Disposable.from(
vscode.commands.registerCommand(
"firebase.dataConnect.moveOperationToConnector",
moveOperationToConnector
)
(number, location, connectorPath) => {
telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.MOVE_TO_CONNECTOR);
moveOperationToConnector(number, location, connectorPath);
},
),
);
}

Expand Down
11 changes: 10 additions & 1 deletion firebase-vscode/src/data-connect/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { dataConnectConfigs } from "./config";
import { createE2eMockable } from "../utils/test_hooks";
import { runCommand } from "./terminal";
import { ExtensionBrokerImpl } from "../extension-broker";
import { DATA_CONNECT_EVENT_NAME } from "../analytics";

function createDeployOnlyCommand(serviceConnectorMap: {
[key: string]: string[];
Expand All @@ -14,14 +15,20 @@ function createDeployOnlyCommand(serviceConnectorMap: {
"firebase deploy --only " +
Object.entries(serviceConnectorMap)
.map(([serviceId, connectorIds]) => {
return `dataconnect:${serviceId}:schema,` + connectorIds.map((connectorId) => `dataconnect:${serviceId}:${connectorId}`).join(",");
return (
`dataconnect:${serviceId}:schema,` +
connectorIds
.map((connectorId) => `dataconnect:${serviceId}:${connectorId}`)
.join(",")
);
})
.join(",")
);
}

export function registerFdcDeploy(
broker: ExtensionBrokerImpl,
telemetryLogger: vscode.TelemetryLogger,
): vscode.Disposable {
const deploySpy = createE2eMockable(
async (...args: Parameters<typeof cliDeploy>) => {
Expand All @@ -33,10 +40,12 @@ export function registerFdcDeploy(
);

const deployAllCmd = vscode.commands.registerCommand("fdc.deploy-all", () => {
telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.DEPLOY_ALL);
runCommand("firebase deploy --only dataconnect");
});

const deployCmd = vscode.commands.registerCommand("fdc.deploy", async () => {
telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.DEPLOY_INDIVIDUAL);
const configs = await firstWhereDefined(dataConnectConfigs).then(
(c) => c.requireValue,
);
Expand Down
Loading

0 comments on commit ffd9502

Please sign in to comment.