diff --git a/packages/manager/package.json b/packages/manager/package.json index 602ec89762..b3d8cebe26 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -97,6 +97,7 @@ "unified": "^10.1.2" }, "devDependencies": { + "@amplitude/experiment-js-client": "1.9.8", "@prismicio/mock": "0.2.0", "@size-limit/preset-small-lib": "8.2.4", "@types/cookie": "0.5.1", diff --git a/packages/manager/src/constants/API_TOKENS.ts b/packages/manager/src/constants/API_TOKENS.ts index dba2aecdf2..afb91e3e22 100644 --- a/packages/manager/src/constants/API_TOKENS.ts +++ b/packages/manager/src/constants/API_TOKENS.ts @@ -2,17 +2,24 @@ import { APPLICATION_MODE } from "./APPLICATION_MODE"; type APITokens = { SegmentKey: string; + AmplitudeKey: string; }; export const API_TOKENS: APITokens = (() => { switch (process.env.SM_ENV) { case APPLICATION_MODE.Development: case APPLICATION_MODE.Staging: - return { SegmentKey: "Ng5oKJHCGpSWplZ9ymB7Pu7rm0sTDeiG" }; + return { + SegmentKey: "Ng5oKJHCGpSWplZ9ymB7Pu7rm0sTDeiG", + AmplitudeKey: "client-rqVU4xTNaz7F51nBfKRUa0K3qnODiqzh", + }; case undefined: case "": case APPLICATION_MODE.Production: - return { SegmentKey: "cGjidifKefYb6EPaGaqpt8rQXkv5TD6P" }; + return { + SegmentKey: "cGjidifKefYb6EPaGaqpt8rQXkv5TD6P", + AmplitudeKey: "client-JuQQWUPimfKWId3WWU6p8xSkTiFqd1qV", + }; default: throw new Error(`Unknown application mode "${process.env.SM_ENV}".`); } diff --git a/packages/manager/src/managers/telemetry/TelemetryManager.ts b/packages/manager/src/managers/telemetry/TelemetryManager.ts index 3cce085cfc..9d874b32e4 100644 --- a/packages/manager/src/managers/telemetry/TelemetryManager.ts +++ b/packages/manager/src/managers/telemetry/TelemetryManager.ts @@ -1,5 +1,5 @@ +import { Experiment, ExperimentClient } from "@amplitude/experiment-js-client"; import { randomUUID } from "node:crypto"; - import { Analytics, GroupParams, TrackParams } from "@segment/analytics-node"; import { readPrismicrc } from "../../lib/prismicrc"; @@ -58,6 +58,7 @@ export class TelemetryManager extends BaseManager { private _anonymousID: string | undefined = undefined; private _userID: string | undefined = undefined; private _context: TelemetryManagerContext | undefined = undefined; + private _experiment: ExperimentClient | undefined = undefined; async initTelemetry(args: TelemetryManagerInitTelemetryArgs): Promise { const isTelemetryEnabled = await this.checkIsTelemetryEnabled(); @@ -82,6 +83,11 @@ export class TelemetryManager extends BaseManager { return analytics; }; + if (isTelemetryEnabled) { + // Start Amplitude Experiment + this.startExperiment(); + } + this._anonymousID = randomUUID(); this._context = { app: { name: args.appName, version: args.appVersion } }; } @@ -268,4 +274,34 @@ export class TelemetryManager extends BaseManager { return readPrismicrc(root).telemetry !== false; } + + async startExperiment(): Promise { + const repositoryName = await this.project.getRepositoryName(); + + this._experiment = Experiment.initialize(API_TOKENS.AmplitudeKey, { + pollOnStart: false, + }); + + await this._experiment + .start({ + user_properties: { + Repository: repositoryName, + }, + }) + .catch((error) => { + console.error("Error starting experiment", error); + }); + } + + async getExperimentValue(variantName: string): Promise { + if (this._experiment) { + await this._experiment.fetch(undefined, { + flagKeys: [variantName], + }); + + return this._experiment.variant(variantName).value; + } + + return undefined; + } } diff --git a/packages/slice-machine/src/features/customTypes/customTypesTable/CustomTypesTable.tsx b/packages/slice-machine/src/features/customTypes/customTypesTable/CustomTypesTable.tsx index 9f37f43c7d..1164f41579 100644 --- a/packages/slice-machine/src/features/customTypes/customTypesTable/CustomTypesTable.tsx +++ b/packages/slice-machine/src/features/customTypes/customTypesTable/CustomTypesTable.tsx @@ -1,5 +1,5 @@ import { type FC } from "react"; -import { Icon, Button, Image, tokens } from "@prismicio/editor-ui"; +import { Icon, Button, Image, tokens, Box, Text } from "@prismicio/editor-ui"; import { useRouter } from "next/router"; import { @@ -30,6 +30,7 @@ import { BlankSlateContent, } from "@src/components/BlankSlate"; import { EditDropdown } from "../EditDropdown"; +import { useExperimentValue } from "@src/hooks/useExperimentValue"; type CustomTypesTableProps = { format: CustomTypeFormat; @@ -50,6 +51,7 @@ export const CustomTypesTable: FC = ({ ); const customTypesConfig = CUSTOM_TYPES_CONFIG[format]; const customTypesMessages = CUSTOM_TYPES_MESSAGES[format]; + const testXavierExperiment = useExperimentValue("test-xavier"); useCustomTypesAutoRevalidation(customTypes, format, updateCustomTypes); @@ -81,6 +83,36 @@ export const CustomTypesTable: FC = ({ return ( <> + + {testXavierExperiment === "on" ? ( + <> + + + This experiment is only displayed because repository name contain + "xru" on it! + + + ) : ( + <> + + + Not part of the experiment because repository name don't contain + "xru" on it! + + + )} + + + diff --git a/packages/slice-machine/src/hooks/useExperimentValue.ts b/packages/slice-machine/src/hooks/useExperimentValue.ts new file mode 100644 index 0000000000..f52885bc9f --- /dev/null +++ b/packages/slice-machine/src/hooks/useExperimentValue.ts @@ -0,0 +1,16 @@ +import { useRequest } from "@prismicio/editor-support/Suspense"; + +import { managerClient } from "@src/managerClient"; + +async function getExperimentValue(variantName: string) { + try { + return await managerClient.telemetry.getExperimentValue(variantName); + } catch (e) { + console.error("Error while trying to get experiment value", e); + return undefined; + } +} + +export function useExperimentValue(variantName: string): string | undefined { + return useRequest(getExperimentValue, [variantName]); +} diff --git a/yarn.lock b/yarn.lock index f3e07bf6c9..e87f7bf92e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,6 +19,42 @@ __metadata: languageName: node linkType: hard +"@amplitude/analytics-connector@npm:^1.5.0": + version: 1.5.0 + resolution: "@amplitude/analytics-connector@npm:1.5.0" + checksum: 157115b642e8b254a88184fe3294150321fdd482388f262bdc08f9ab0c68b01b5f10d2e35a924edb8a8380f0944f8b1363133c2a3f42b59e33da319bfe21c8fa + languageName: node + linkType: hard + +"@amplitude/experiment-core@npm:^0.7.1": + version: 0.7.1 + resolution: "@amplitude/experiment-core@npm:0.7.1" + dependencies: + js-base64: ^3.7.5 + checksum: a4a7f1a2db8e94e35c2ff3af2f58686d8aebfcd748a70e41b0751f7b569dc95a9b9670b5185d432b130351f8725696d8c1fcac6e08451e2fdb9e62bc5590c5a7 + languageName: node + linkType: hard + +"@amplitude/experiment-js-client@npm:1.9.8": + version: 1.9.8 + resolution: "@amplitude/experiment-js-client@npm:1.9.8" + dependencies: + "@amplitude/analytics-connector": ^1.5.0 + "@amplitude/experiment-core": ^0.7.1 + "@amplitude/ua-parser-js": ^0.7.31 + base64-js: 1.5.1 + unfetch: 4.1.0 + checksum: 0b76062c1d5fa36f2edcbcf8ffccf4adf919291e02b064e190698b4ee56258fe91af6f4c7a8e7bd5bcbfd29d82e4c65e713d84a9e80b580a3d731f7ed61df457 + languageName: node + linkType: hard + +"@amplitude/ua-parser-js@npm:^0.7.31": + version: 0.7.33 + resolution: "@amplitude/ua-parser-js@npm:0.7.33" + checksum: b08ce4cd4e96fed9eebffadb4060d24751cea80caf4265e6b829b7cfdb6561431386015674b3ac330a8987b990e2ca70725d4298eb1438beccb85264848b0b34 + languageName: node + linkType: hard + "@ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.2.1": version: 2.2.1 resolution: "@ampproject/remapping@npm:2.2.1" @@ -8433,6 +8469,7 @@ __metadata: version: 0.0.0-use.local resolution: "@slicemachine/manager@workspace:packages/manager" dependencies: + "@amplitude/experiment-js-client": 1.9.8 "@antfu/ni": ^0.20.0 "@prismicio/custom-types-client": ^1.2.0 "@prismicio/mock": 0.2.0 @@ -13356,7 +13393,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": +"base64-js@npm:1.5.1, base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -21657,6 +21694,13 @@ __metadata: languageName: node linkType: hard +"js-base64@npm:^3.7.5": + version: 3.7.5 + resolution: "js-base64@npm:3.7.5" + checksum: 67a78c8b1c47b73f1c6fba1957e9fe6fd9dc78ac93ac46cc2e43472dcb9cf150d126fb0e593192e88e0497354fa634d17d255add7cc6ee3c7b4d29870faa8e18 + languageName: node + linkType: hard + "js-levenshtein@npm:^1.1.6": version: 1.1.6 resolution: "js-levenshtein@npm:1.1.6" @@ -32888,6 +32932,13 @@ __metadata: languageName: node linkType: hard +"unfetch@npm:4.1.0": + version: 4.1.0 + resolution: "unfetch@npm:4.1.0" + checksum: 8a0fee1e0f6ad8b3a2966fa199d07716affc3682d8e1c2c0cc138e5e5d2a2e0627d8c3321a4529a79e8a58332955bf80ac6c018f1dcc6de652026a7c3257d726 + languageName: node + linkType: hard + "unfetch@npm:^4.2.0": version: 4.2.0 resolution: "unfetch@npm:4.2.0"