microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.7.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/experimentService/experimentService.ts

150lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as vscode from "vscode";
5import { IExperiment } from "./IExperiment";
6import { TelemetryHelper } from "../../common/telemetryHelper";
7import { Telemetry } from "../../common/telemetry";
8import { ExtensionConfigManager } from "../extensionConfigManager";
9import { IConfig, retryDownloadConfig } from "../remoteConfigHelper";
10
11export enum ExperimentStatuses {
12 ENABLED = "enabled",
13 DISABLED = "disabled",
14 FAILED = "failed",
15}
16
17export interface ExperimentConfig extends IConfig {
18 experimentName: string;
19 popCoveragePercent: number;
20 enabled: boolean;
21}
22
23export interface ExperimentParameters extends ExperimentConfig {
24 [key: string]: any;
25 extensionId?: string;
26}
27
28export interface ExperimentResult {
29 resultStatus: ExperimentStatuses;
30 updatedExperimentParameters: ExperimentParameters;
31 error?: Error;
32}
33
34export class ExperimentService implements vscode.Disposable {
35 private static instance: ExperimentService;
36
37 private readonly endpointURL: string;
38 private downloadedExperimentsConfig: Array<ExperimentConfig> | null;
39 private experimentsInstances: Map<string, IExperiment>;
40 private downloadConfigRequest: Promise<ExperimentConfig[]>;
41 private cancellationTokenSource: vscode.CancellationTokenSource;
42
43 public static create(): ExperimentService {
44 if (!ExperimentService.instance) {
45 ExperimentService.instance = new ExperimentService();
46 }
47
48 return ExperimentService.instance;
49 }
50
51 public async runExperiments(): Promise<void> {
52 if (!this.downloadedExperimentsConfig) {
53 this.downloadedExperimentsConfig = await this.downloadConfigRequest;
54 this.experimentsInstances = await this.initializeExperimentsInstances();
55 }
56
57 let experimentResults: Array<ExperimentResult> = await Promise.all(
58 this.downloadedExperimentsConfig.map(expConfig => this.executeExperiment(expConfig)),
59 );
60
61 this.sendExperimentTelemetry(experimentResults);
62 }
63
64 public dispose(): void {
65 this.cancellationTokenSource.cancel();
66 this.cancellationTokenSource.dispose();
67 }
68
69 private constructor() {
70 this.endpointURL =
71 "https://microsoft.github.io/vscode-react-native/experiments/experimentsConfig.json";
72 this.cancellationTokenSource = new vscode.CancellationTokenSource();
73 this.downloadedExperimentsConfig = null;
74
75 this.downloadConfigRequest = retryDownloadConfig<ExperimentConfig[]>(
76 this.endpointURL,
77 this.cancellationTokenSource,
78 );
79 }
80
81 private async executeExperiment(expConfig: ExperimentConfig): Promise<ExperimentResult> {
82 let curExperimentParameters = ExtensionConfigManager.config.get(expConfig.experimentName);
83 let expInstance = this.experimentsInstances.get(expConfig.experimentName);
84
85 let expResult: ExperimentResult;
86 if (expInstance && expConfig.enabled) {
87 try {
88 expResult = await expInstance.run(expConfig, curExperimentParameters);
89 ExtensionConfigManager.config.set(
90 expConfig.experimentName,
91 expResult.updatedExperimentParameters,
92 );
93 } catch (err) {
94 expResult = {
95 resultStatus: ExperimentStatuses.FAILED,
96 updatedExperimentParameters: expConfig,
97 error: err,
98 };
99 }
100 } else {
101 expResult = {
102 resultStatus: ExperimentStatuses.DISABLED,
103 updatedExperimentParameters: expConfig,
104 };
105 }
106
107 return expResult;
108 }
109
110 private async initializeExperimentsInstances(): Promise<Map<string, IExperiment>> {
111 let expInstances = new Map<string, IExperiment>();
112
113 if (this.downloadedExperimentsConfig) {
114 for (let expConfig of this.downloadedExperimentsConfig) {
115 try {
116 let expClass = await import(`./experiments/${expConfig.experimentName}`);
117 expInstances.set(expConfig.experimentName, new expClass.default());
118 } catch (err) {
119 expConfig.enabled = false;
120 }
121 }
122 }
123
124 return expInstances;
125 }
126
127 private sendExperimentTelemetry(experimentsResults: ExperimentResult[]): void {
128 const runExperimentsEvent = TelemetryHelper.createTelemetryEvent("runExperiments");
129
130 experimentsResults.forEach(expResult => {
131 if (expResult.resultStatus === ExperimentStatuses.FAILED && expResult.error) {
132 TelemetryHelper.addTelemetryEventErrorProperty(
133 runExperimentsEvent,
134 expResult.error,
135 undefined,
136 `${expResult.updatedExperimentParameters.experimentName}.`,
137 );
138 } else {
139 TelemetryHelper.addTelemetryEventProperty(
140 runExperimentsEvent,
141 expResult.updatedExperimentParameters.experimentName,
142 expResult.resultStatus,
143 false,
144 );
145 }
146 });
147
148 Telemetry.send(runExperimentsEvent);
149 }
150}
151