Skip to content

Commit 8ecaa79

Browse files
committed
[dashboard] Fix UI experiments
1 parent 7b7193d commit 8ecaa79

File tree

2 files changed

+44
-33
lines changed

2 files changed

+44
-33
lines changed

components/dashboard/src/Analytics.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ interface TrackPathChanged {
4747
}
4848

4949
interface TrackUIExperiments {
50-
ui_experiments?: string[],
50+
ui_experiments?: {},
5151
}
5252

5353
//call this to track all events outside of button and anchor clicks
@@ -56,7 +56,7 @@ export const trackEvent = (event: Event, properties: EventProperties) => {
5656
}
5757

5858
const trackEventInternal = (event: InternalEvent, properties: InternalEventProperties, userKnown?: boolean) => {
59-
properties.ui_experiments = Experiment.getAsArray();
59+
properties.ui_experiments = Experiment.get();
6060

6161
getGitpodService().server.trackEvent({
6262
//if the user is authenticated, let server determine the id. else, pass anonymousId explicitly.
@@ -141,7 +141,7 @@ export const trackLocation = async (userKnown: boolean) => {
141141
path: window.location.pathname,
142142
host: window.location.hostname,
143143
url: window.location.href,
144-
ui_experiments: Experiment.getAsArray(),
144+
ui_experiments: Experiment.get(),
145145
};
146146

147147
getGitpodService().server.trackLocation({

components/dashboard/src/experiments.ts

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,57 +30,68 @@ const Experiments = {
3030
// "example": 0.1,
3131
"login-from-context-6826": 0.5, // https://github.com/gitpod-io/gitpod/issues/6826
3232
};
33-
const ExperimentsSet = new Set(Object.keys(Experiments)) as Set<Experiment>;
33+
type Experiments = Partial<{ [e in Experiment]: boolean }>;
3434
export type Experiment = keyof (typeof Experiments);
3535

3636
export namespace Experiment {
37-
export function seed(keepCurrent: boolean): Set<Experiment> {
38-
const current = keepCurrent ? get() : undefined;
39-
40-
// add all current experiments to ensure stability
41-
const result = new Set<Experiment>([...(current || [])].filter(e => ExperimentsSet.has(e)));
37+
/**
38+
* Randomly decides what the set of Experiments is the user participates in
39+
* @param keepCurrent
40+
* @returns Experiments
41+
*/
42+
export function seed(keepCurrent: boolean): Experiments {
43+
const result = keepCurrent ? get() || {} : {};
4244

43-
// identify all new experiments and add if random
44-
const newExperiment = new Set<Experiment>([...ExperimentsSet].filter(e => !result.has(e)));
45-
for (const e of newExperiment) {
46-
if (Math.random() < Experiments[e]) {
47-
result.add(e);
45+
for (const experiment of Object.keys(Experiments) as Experiment[]) {
46+
if (!(experiment in result)) {
47+
result[experiment] = Math.random() < Experiments[experiment];
4848
}
4949
}
5050

5151
return result;
5252
}
5353

54-
export function set(set: Set<Experiment>): void {
54+
export function set(set: Experiments): void {
5555
try {
56-
const arr = Array.from(set);
57-
window.localStorage.setItem(UI_EXPERIMENTS_KEY, JSON.stringify(arr));
56+
window.localStorage.setItem(UI_EXPERIMENTS_KEY, JSON.stringify(set));
5857
} catch (err) {
59-
console.error(`error setting ${UI_EXPERIMENTS_KEY}`, err);
58+
console.warn(`error setting ${UI_EXPERIMENTS_KEY}`, err);
6059
}
6160
}
6261

6362
export function has(experiment: Experiment): boolean {
64-
const set = get();
65-
if (!set) {
63+
try {
64+
const set = get();
65+
if (!set) {
66+
return false;
67+
}
68+
return set[experiment] === true;
69+
} catch (err) {
70+
console.warn(`error checking experiment '${experiment}'`, err);
6671
return false;
6772
}
68-
return set.has(experiment);
6973
}
7074

71-
export function get(): Set<Experiment> | undefined {
72-
const arr = window.localStorage.getItem(UI_EXPERIMENTS_KEY);
73-
if (arr === null) {
74-
return undefined;
75-
}
76-
return new Set(JSON.parse(arr)) as Set<Experiment>;
77-
}
75+
/** Retrieves all currently valid Experiments from localStorage */
76+
export function get(): Experiments | undefined {
77+
try {
78+
const objStr = window.localStorage.getItem(UI_EXPERIMENTS_KEY);
79+
if (objStr === null) {
80+
return undefined;
81+
}
7882

79-
export function getAsArray(): Experiment[] {
80-
const set = get();
81-
if (!set) {
82-
return [];
83+
const obj = JSON.parse(objStr) as Experiments;
84+
// trim to contain only known keys so we're type-safe
85+
for (const e of Object.keys(obj)) {
86+
if (!(e in Experiments)) {
87+
delete (obj as any)[e];
88+
}
89+
}
90+
return obj;
91+
} catch (err) {
92+
// we definitely don't want to break anybody because of weird errors
93+
console.warn(`error getting ${UI_EXPERIMENTS_KEY}`, err);
94+
return undefined;
8395
}
84-
return Array.from(set);
8596
}
8697
}

0 commit comments

Comments
 (0)