Skip to content

Commit

Permalink
Separate identify call function
Browse files Browse the repository at this point in the history
  • Loading branch information
noeldevelops committed Oct 18, 2024
1 parent ce8247e commit 4ae40ce
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 33 deletions.
12 changes: 5 additions & 7 deletions src/authProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { getStorageManager } from "./storage";
import { AUTH_COMPLETED_KEY, AUTH_SESSION_EXISTS_KEY } from "./storage/constants";
import { getResourceManager } from "./storage/resourceManager";
import { getUriHandler } from "./uriHandler";
import { getTelemetryLogger } from "./telemetry";
import { sendTelemetryIdentifyEvent } from "./telemetry";

const logger = new Logger("authProvider");

Expand Down Expand Up @@ -154,12 +154,10 @@ export class ConfluentCloudAuthProvider implements vscode.AuthenticationProvider

// User logged in successfully so we send an identify event to Segment
if (authenticatedConnection.status.authentication.user) {
const userEmail = new vscode.TelemetryTrustedValue(
authenticatedConnection.status.authentication.user,
);
getTelemetryLogger().logUsage("Signed In", {
identify: true,
user: { ...authenticatedConnection.status.authentication.user, username: userEmail },
sendTelemetryIdentifyEvent({
eventName: "Signed In",
userInfo: authenticatedConnection.status.authentication.user,
session: undefined,
});
}
// we want to continue regardless of whether or not the user dismisses the notification,
Expand Down
15 changes: 13 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import { getCCloudAuthSession } from "./sidecar/connections";
import { StorageManager } from "./storage";
import { CCloudResourcePreloader } from "./storage/ccloudPreloader";
import { migrateStorageIfNeeded } from "./storage/migrationManager";
import { getTelemetryLogger } from "./telemetry";
import { getTelemetryLogger, sendTelemetryIdentifyEvent } from "./telemetry";
import { getUriHandler } from "./uriHandler";
import { ResourceViewProvider } from "./viewProviders/resources";
import { SchemasViewProvider } from "./viewProviders/schemas";
Expand Down Expand Up @@ -251,7 +251,18 @@ async function setupAuthProvider(): Promise<vscode.Disposable[]> {
]);

// attempt to get a session to trigger the initial auth badge for signing in
await getCCloudAuthSession();
const cloudSession = await getCCloudAuthSession();

if (cloudSession) {
logger.info("Initial CCloud auth session found");
sendTelemetryIdentifyEvent({
eventName: "Activated With Session",
userInfo: undefined,
session: cloudSession,
});
} else {
logger.info("No initial CCloud auth session found");
}

return disposables;
}
Expand Down
55 changes: 31 additions & 24 deletions src/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ let warnedAboutSegmentKey = false;
* Use Proper Case, Noun + Past Tense Verb to represent the user's action (e.g. "Order Completed", "File Downloaded", "User Registered")
* Optionally, add any relevant data as the second parameter
*
* For IDENTIFY calls - add the "identify" key to the data along with a user object (with at least an id) to send an identify type call.
* For IDENTIFY calls - use sendTelemetryIdentifyEvent with a user object (with at least an id) to send an identify type call.
* ```
* getTelemetryLogger().logUsage("Event That Triggered Identify", { identify: true, user: { id: "123", ...} });"
* sendTelemetryIdentifyEvent({eventName: "Event That Triggered Identify", userInfo: { id: "123", ...} });"
* ```
* It will send an Identify call followed by a Track event per this Segment recommendation:
* "Whenever possible, follow the Identify call with a Track event that records what caused the user to be identified."
Expand Down Expand Up @@ -66,30 +66,28 @@ export function getTelemetryLogger(): vscode.TelemetryLogger {
}
analytics = new Analytics({ writeKey, disable: false });
}
// We extract the vscode session ID from the event data, but this random id will be sent if it is undefined (unlikely but not guranteed by the type def)

segmentAnonId = randomUUID();

telemetryLogger = vscode.env.createTelemetryLogger({
sendEventData: (eventName, data) => {
// Remove the prefix that vscode adds to event names
const cleanEventName = eventName.replace(/^confluentinc\.vscode-confluent\//, "");
// Extract & save the user id if was sent
if (data?.user) userId = data.user.id;
// To send an identify call, include `identify: true` and a user object in the logUsage call data
if (data?.user?.id) userId = data.user.id;
if (data?.identify && data?.user) {
const traits = prepareSegmentIdentifyTraits(data.user as UserInfo);
analytics?.identify({
userId,
anonymousId: data?.["common.vscodesessionid"] || segmentAnonId,
traits,
anonymousId: segmentAnonId,
...data.user,
});
// We don't want to send the user traits or identify prop in the following Track call
delete data.identify;
delete data.user;
}
analytics?.track({
userId,
anonymousId: data?.["common.vscodesessionid"] || segmentAnonId,
anonymousId: segmentAnonId,
event: cleanEventName,
properties: { currentSidecarVersion, ...data }, // VSCode Common properties in data includes the extension version
});
Expand All @@ -114,23 +112,32 @@ export function checkTelemetrySettings(event: Sentry.Event) {
return event;
}

type SegmentIdentifyTraits = {
social_connection?: string;
username?: string;
domain?: string;
};

function prepareSegmentIdentifyTraits(userInfo: UserInfo): SegmentIdentifyTraits {
let traits: SegmentIdentifyTraits = {};
if (userInfo.social_connection) traits["social_connection"] = userInfo.social_connection;
if (userInfo.username) {
traits["username"] = userInfo.username;
// Validate email and extract domain
/** Given authenticated session and/or user, process user information & send an Identify event to Segment via TelemetryLogger*/
export function sendTelemetryIdentifyEvent({
eventName,
userInfo,
session,
}: {
eventName: string;
userInfo: UserInfo | undefined;
session: vscode.AuthenticationSession | undefined;
}) {
const id = userInfo?.id || session?.account.id;
const username = userInfo?.username || session?.account.label;
const social_connection = userInfo?.social_connection;
let domain: string | undefined;
if (username) {
// email is redacted by VSCode TelemetryLogger, but we extract domain for Confluent analytics use
const emailRegex = /@[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+/;
const match = userInfo.username.match(emailRegex);
const match = username.match(emailRegex);
if (match) {
traits["domain"] = userInfo.username.split("@")[1];
domain = username.split("@")[1];
}
}
return traits;
if (id) {
getTelemetryLogger().logUsage(eventName, {
identify: true,
user: { id, domain, social_connection },
});
}
}

0 comments on commit 4ae40ce

Please sign in to comment.