Skip to content

Commit

Permalink
Refactored async initialization logic for Mixpanel, Grafana, and Laun…
Browse files Browse the repository at this point in the history
…ch Darkly
  • Loading branch information
bvaughn committed Jul 10, 2024
1 parent 0bdfaa5 commit 0f0f2e6
Show file tree
Hide file tree
Showing 48 changed files with 1,207 additions and 1,079 deletions.
18 changes: 7 additions & 11 deletions packages/cypress/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/// <reference types="cypress" />

import { logger } from "@replay-cli/shared/logger";
import { mixpanelAPI } from "@replay-cli/shared/mixpanel/mixpanelAPI";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { getRuntimePath } from "@replay-cli/shared/runtime/getRuntimePath";
import { setUserAgent } from "@replay-cli/shared/userAgent";
import { initializeSession } from "@replay-cli/shared/session/initializeSession";
import { RecordingEntry, initMetadataFile, warn } from "@replayio/test-utils";
import chalk from "chalk";
import path from "path";
Expand Down Expand Up @@ -159,7 +159,7 @@ async function onAfterRun() {

if (missingSteps) {
logger.error("OnAfterRun:AfterRunMissingSteps", { missingSteps });
mixpanelAPI.trackEvent("warning.missing-steps");
mixpanelClient.trackEvent("warning.missing-steps");
loudWarning(
"Your tests completed but our plugin did not receive any command events.",
"",
Expand Down Expand Up @@ -197,7 +197,7 @@ function onReplayTask(value: any) {
reporter.addStep(v);
} else {
logger.error("OnReplayTask:ReplayTaskUnexpectedPayload", { payload: v });
mixpanelAPI.trackEvent("error.replay-task-unexpected-payload", { payload: v });
mixpanelClient.trackEvent("error.replay-task-unexpected-payload", { payload: v });
}
});

Expand Down Expand Up @@ -261,11 +261,7 @@ const plugin = (
config: Cypress.PluginConfigOptions,
options: PluginOptions = {}
) => {
setUserAgent(`${packageName}/${packageVersion}`);

logger.initialize(packageName, packageVersion);
logger.identify(getAuthKey(config));
mixpanelAPI.initialize({
initializeSession({
accessToken: getAuthKey(config),
packageName,
packageVersion,
Expand All @@ -283,7 +279,7 @@ const plugin = (

ws.on("error", error => {
logger.error("CypressPlugin:WebSocketError", { error });
mixpanelAPI.trackEvent("error.websocket-error", { error });
mixpanelClient.trackEvent("error.websocket-error", { error });
warn("WebSocket error", error);
});

Expand All @@ -294,7 +290,7 @@ const plugin = (
onReplayTask(obj.events);
} catch (error) {
logger.error("CypressPlugin:WebSocketMessageError", { error });
mixpanelAPI.trackEvent("error.websocket-message-error", { error });
mixpanelClient.trackEvent("error.websocket-message-error", { error });
warn("Error parsing message from test", error);
}
});
Expand Down
14 changes: 9 additions & 5 deletions packages/jest/src/runner.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { JestEnvironment } from "@jest/environment";
import type { TestFileEvent, TestResult } from "@jest/test-result";
import type { Circus, Config } from "@jest/types";
import { logger } from "@replay-cli/shared/logger";
import { setUserAgent } from "@replay-cli/shared/userAgent";
import { initializeSession } from "@replay-cli/shared/session/initializeSession";
import {
ReplayReporter,
getAccessToken,
getMetadataFilePath as getMetadataFilePathBase,
initMetadataFile,
removeAnsiCodes,
} from "@replayio/test-utils";
import type Runtime from "jest-runtime";
import path from "path";
import * as pkgJson from "../package.json";
import { name as packageName, version as packageVersion } from "../package.json";

const runner = require("jest-circus/runner");
const pluginVersion = require("@replayio/jest/package.json").version;
Expand All @@ -20,6 +20,12 @@ export function getMetadataFilePath(workerIndex = 0) {
return getMetadataFilePathBase("JEST", workerIndex);
}

initializeSession({
accessToken: getAccessToken(),
packageName,
packageVersion,
});

let version: string | undefined;

type MatcherResult = {
Expand All @@ -38,8 +44,6 @@ const ReplayRunner = async (
testPath: string,
sendMessageToJest?: TestFileEvent
): Promise<TestResult> => {
setUserAgent(`${pkgJson.name}/${pkgJson.version}`);
logger.initialize(pkgJson.name, pkgJson.version);
if (!version) {
try {
version = require(require.resolve("jest/package.json", {
Expand Down
18 changes: 7 additions & 11 deletions packages/playwright/src/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import type {
TestResult,
} from "@playwright/test/reporter";
import { logger } from "@replay-cli/shared/logger";
import { mixpanelAPI } from "@replay-cli/shared/mixpanel/mixpanelAPI";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { getRuntimePath } from "@replay-cli/shared/runtime/getRuntimePath";
import { initializeSession } from "@replay-cli/shared/session/initializeSession";
import { emphasize, highlight, link } from "@replay-cli/shared/theme";
import { setUserAgent } from "@replay-cli/shared/userAgent";
import {
ReplayReporter,
ReplayReporterConfig,
Expand Down Expand Up @@ -83,18 +83,14 @@ export default class ReplayPlaywrightReporter implements Reporter {
private _executedProjects: Record<string, { usesReplayBrowser: boolean }> = {};

constructor(config: ReplayPlaywrightConfig) {
setUserAgent(`${packageName}/${packageVersion}`);

logger.initialize(packageName, packageVersion);
logger.identify(getAccessToken(config));
mixpanelAPI.initialize({
initializeSession({
accessToken: getAccessToken(config),
packageName,
packageVersion,
});

if (!config || typeof config !== "object") {
mixpanelAPI.trackEvent("error.invalid-reporter-config", { config });
mixpanelClient.trackEvent("error.invalid-reporter-config", { config });

throw new Error(
`Expected an object for @replayio/playwright/reporter configuration but received: ${config}`
Expand Down Expand Up @@ -374,13 +370,13 @@ export default class ReplayPlaywrightReporter implements Reporter {
const output: string[] = [];

if (!didUseReplayBrowser) {
mixpanelAPI.trackEvent("warning.reporter-used-without-replay-project");
mixpanelClient.trackEvent("warning.reporter-used-without-replay-project");
output.push(emphasize("None of the configured projects ran using Replay Chromium."));
}

if (!isReplayBrowserInstalled) {
if (didUseReplayBrowser) {
mixpanelAPI.trackEvent("warning.replay-browser-not-installed");
mixpanelClient.trackEvent("warning.replay-browser-not-installed");
}

output.push(
Expand Down Expand Up @@ -418,7 +414,7 @@ export default class ReplayPlaywrightReporter implements Reporter {
// the issue is tracked here: https://github.com/microsoft/playwright/issues/23875
console.log("");
} finally {
await Promise.all([mixpanelAPI.close().catch(noop), logger.close().catch(noop)]);
await Promise.all([mixpanelClient.close().catch(noop), logger.close().catch(noop)]);
}
}

Expand Down
13 changes: 8 additions & 5 deletions packages/puppeteer/src/install.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { logger } from "@replay-cli/shared/logger";
import { installLatestRuntimeRelease } from "@replay-cli/shared/runtime/installLatestRuntimeRelease";
import { name, version } from "../package.json";
import { getAccessToken } from "@replay-cli/shared/authentication/getAccessToken";
import { initializeSession } from "@replay-cli/shared/session/initializeSession";
import { getAccessToken } from "@replayio/test-utils";
import { name as packageName, version as packageVersion } from "../package.json";

export default async function install() {
try {
logger.initialize(name, version);
const accessToken = await getAccessToken();
await logger.identify(accessToken.accessToken);
initializeSession({
accessToken: getAccessToken(),
packageName,
packageVersion,
});
} catch (error) {
logger.error("Failed to identify for logger", { error });
}
Expand Down
15 changes: 10 additions & 5 deletions packages/replayio/src/bin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { logger } from "@replay-cli/shared/logger";
import { exitProcess } from "@replay-cli/shared/process/exitProcess";
import { setUserAgent } from "@replay-cli/shared/userAgent";
import { name, version } from "../package.json";
import { name as packageName, version as packageVersion } from "../package.json";
import { finalizeCommander } from "./utils/commander/finalizeCommander";

// Commands self-register with "commander"
import { getAccessToken } from "@replay-cli/shared/authentication/getAccessToken";
import { initializeSession } from "@replay-cli/shared/session/initializeSession";
import "./commands/info";
import "./commands/list";
import "./commands/login";
Expand All @@ -17,9 +18,13 @@ import "./commands/upload";
import "./commands/upload-source-maps";
import "./commands/whoami";

logger.initialize(name, version);

setUserAgent(`${name}/${version}`);
getAccessToken().then(({ accessToken }) => {
initializeSession({
accessToken,
packageName,
packageVersion,
});
});

finalizeCommander();

Expand Down
4 changes: 2 additions & 2 deletions packages/replayio/src/commands/record.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ProcessError } from "@replay-cli/shared/ProcessError";
import { mixpanelAPI } from "@replay-cli/shared/mixpanel/mixpanelAPI";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { exitProcess } from "@replay-cli/shared/process/exitProcess";
import { canUpload } from "@replay-cli/shared/recording/canUpload";
import { getRecordings } from "@replay-cli/shared/recording/getRecordings";
Expand Down Expand Up @@ -103,7 +103,7 @@ async function record(url: string = "about:blank") {
console.log(""); // Spacing for readability
}

mixpanelAPI.trackEvent("record.results", {
mixpanelClient.trackEvent("record.results", {
crashedCount: crashedRecordings.length,
successCountsByType: finishedRecordings.reduce(
(map, recording) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/replayio/src/commands/whoami.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getAccessToken } from "@replay-cli/shared/authentication/getAccessToken";
import { getAuthInfo } from "@replay-cli/shared/graphql/getAuthInfo";
import { getAuthInfo } from "@replay-cli/shared/authentication/getAuthInfo";
import { exitProcess } from "@replay-cli/shared/process/exitProcess";
import { dim, emphasize, highlight, link } from "@replay-cli/shared/theme";
import { name as packageName } from "../../package.json";
Expand Down
4 changes: 2 additions & 2 deletions packages/replayio/src/utils/browser/reportBrowserCrash.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAccessToken } from "@replay-cli/shared/authentication/getAccessToken";
import { getReplayPath } from "@replay-cli/shared/getReplayPath";
import { logger } from "@replay-cli/shared/logger";
import { getUserAgent } from "@replay-cli/shared/userAgent";
import { getUserAgent } from "@replay-cli/shared/session/getUserAgent";
import { readFile, writeFileSync } from "fs-extra";
import { File, FormData, fetch } from "undici";
import { replayApiServer } from "../../config";
Expand All @@ -21,7 +21,7 @@ export async function reportBrowserCrash(stderr: string) {
};
}

const userAgent = getUserAgent();
const userAgent = await getUserAgent();

const formData = new FormData();

Expand Down
4 changes: 2 additions & 2 deletions packages/replayio/src/utils/commander/registerCommand.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mixpanelAPI } from "@replay-cli/shared/mixpanel/mixpanelAPI";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { program } from "commander";
import { initialize } from "../initialization/initialize";

Expand All @@ -17,7 +17,7 @@ export function registerCommand(
} = config;

return program.command(commandName).hook("preAction", async () => {
mixpanelAPI.trackEvent("command", { commandName });
mixpanelClient.trackEvent("command", { commandName });

await initialize({
checkForNpmUpdate,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { logger } from "@replay-cli/shared/logger";
import { createAsyncFunctionWithTracking } from "@replay-cli/shared/mixpanel/createAsyncFunctionWithTracking";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { fetch } from "undici";
import { version as currentVersion, name as packageName } from "../../../package.json";
import { shouldPrompt } from "../prompt/shouldPrompt";
import { UpdateCheck } from "./types";

const PROMPT_ID = "npm-update";

export const checkForNpmUpdate = createAsyncFunctionWithTracking(
export const checkForNpmUpdate = mixpanelClient.createAsyncFunctionWithTracking(
async function checkForNpmUpdate(): Promise<UpdateCheck<string>> {
try {
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-metadata-format
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { logger } from "@replay-cli/shared/logger";
import { createAsyncFunctionWithTracking } from "@replay-cli/shared/mixpanel/createAsyncFunctionWithTracking";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { existsSync } from "fs-extra";
import { getBrowserPath } from "../browser/getBrowserPath";
import { getLatestRelease } from "../installation/getLatestReleases";
Expand All @@ -15,7 +15,7 @@ export type Version = {
version: Release["version"];
};

export const checkForRuntimeUpdate = createAsyncFunctionWithTracking(
export const checkForRuntimeUpdate = mixpanelClient.createAsyncFunctionWithTracking(
async function checkForRuntimeUpdate(): Promise<UpdateCheck<Version>> {
let latestRelease: Release;
let latestBuildId: string;
Expand Down
27 changes: 0 additions & 27 deletions packages/replayio/src/utils/initialization/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { raceWithTimeout } from "@replay-cli/shared/async/raceWithTimeout";
import { getAccessToken } from "@replay-cli/shared/authentication/getAccessToken";
import { initLaunchDarklyFromAccessToken } from "@replay-cli/shared/launch-darkly/initLaunchDarklyFromAccessToken";
import { mixpanelAPI } from "@replay-cli/shared/mixpanel/mixpanelAPI";
import { name as packageName, version as packageVersion } from "../../../package.json";
import { logPromise } from "../async/logPromise";
import { checkForNpmUpdate } from "./checkForNpmUpdate";
import { checkForRuntimeUpdate } from "./checkForRuntimeUpdate";
import { promptForAuthentication } from "./promptForAuthentication";
import { promptForNpmUpdate } from "./promptForNpmUpdate";
import { promptForRuntimeUpdate } from "./promptForRuntimeUpdate";
import { logger } from "@replay-cli/shared/logger";

export async function initialize({
checkForNpmUpdate: shouldCheckForNpmUpdate,
Expand Down Expand Up @@ -47,34 +43,11 @@ export async function initialize({
accessToken = await promptForAuthentication();
}

// Initialize LaunchDarkly and Mixpanel for authenticated users
// These tasks don't print anything so they can be done in parallel with the upgrade prompts
// They also shouldn't block on failure, so we should only wait a couple of seconds before giving up
const abortController = new AbortController();

const loggerPromise = raceWithTimeout(logger.identify(accessToken), 2_500, abortController);

const launchDarklyPromise = accessToken
? raceWithTimeout(
initLaunchDarklyFromAccessToken(accessToken, abortController.signal),
2_500,
abortController
)
: Promise.resolve();

const mixpanelPromise = raceWithTimeout(
mixpanelAPI.initialize({ accessToken, packageName, packageVersion }),
2_500,
abortController
);

if (npmUpdateCheck.hasUpdate && npmUpdateCheck.shouldShowPrompt) {
await promptForNpmUpdate(npmUpdateCheck);
}

if (runtimeUpdateCheck.hasUpdate && runtimeUpdateCheck.shouldShowPrompt) {
await promptForRuntimeUpdate(runtimeUpdateCheck);
}

await Promise.all([loggerPromise, launchDarklyPromise, mixpanelPromise]);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mixpanelAPI } from "@replay-cli/shared/mixpanel/mixpanelAPI";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { emphasize } from "@replay-cli/shared/theme";
import { name as packageName } from "../../../package.json";
import { installLatestRelease } from "../installation/installLatestRelease";
Expand Down Expand Up @@ -50,7 +50,7 @@ export async function promptForRuntimeUpdate(updateCheck: UpdateCheckResult<Vers
// A failed install will be handled later
}
} else {
mixpanelAPI.trackEvent("update.runtime.skipped", { newRuntimeVersion: toVersion });
mixpanelClient.trackEvent("update.runtime.skipped", { newRuntimeVersion: toVersion });
}

console.log("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { writeToCache } from "@replay-cli/shared/cache";
import { getReplayPath } from "@replay-cli/shared/getReplayPath";
import { logger } from "@replay-cli/shared/logger";
import { createAsyncFunctionWithTracking } from "@replay-cli/shared/mixpanel/createAsyncFunctionWithTracking";
import { mixpanelClient } from "@replay-cli/shared/mixpanelClient";
import { dim, link } from "@replay-cli/shared/theme";
import { spawnSync } from "child_process";
import { ensureDirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "fs-extra";
Expand All @@ -21,7 +21,7 @@ type Result = {
forkedVersion: string | null;
};

export const installLatestRelease = createAsyncFunctionWithTracking(
export const installLatestRelease = mixpanelClient.createAsyncFunctionWithTracking(
async function installLatestRelease(): Promise<Result | undefined> {
logger.info("InstallLatestRelease:Start");
const runtimeBaseDir = getReplayPath("runtimes");
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/src/async/isPromiseLike.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isPromiseLike<Type>(value: any): value is PromiseLike<Type> {
return value && typeof value.then === "function";
}
Loading

0 comments on commit 0f0f2e6

Please sign in to comment.