blob: 9378b97352bb0c55b86157342b8bcabab4df75fd [file]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { randomUUID } from 'crypto';
import * as vscode from 'vscode';
import * as metric_properties from './metric_properties';
const ENABLED_PROPERTY = 'analytics-status';
const UUID_PROPERTY = 'uuid';
const LAUNCHED_PROPERTY = 'vscode-fuchsia-launched';
const INTERNAL_PROPERTY = 'analytics-status-internal';
export namespace external {
/**
* Enable analytics in persistent state for external user.
*/
export async function enable(): Promise<void> {
await metric_properties.setBoolean(ENABLED_PROPERTY, true);
await metric_properties.set(UUID_PROPERTY, randomUUID());
}
/**
* Disable analytics in persistent state for external user.
*/
export async function disable(): Promise<void> {
await metric_properties.setBoolean(ENABLED_PROPERTY, false);
await metric_properties.deleteProperty(UUID_PROPERTY);
}
/**
* Returns true if analytics' persistent state is enabled for external user.
*/
export async function isEnabled(): Promise<boolean> {
// Treat undefined as false
return (await metric_properties.getBoolean(ENABLED_PROPERTY)) === true;
}
/**
* Returns true if the extension the first launch of the first Fuchsia tool,
* i.e. the extension is launched before other Fuchsia tools such as fx, ffx.
*/
export async function isFirstLaunchOfFirstTool(): Promise<boolean> {
return !(await metric_properties.exists(ENABLED_PROPERTY));
}
/**
* Mark that the extension has been haunched
*/
export async function markAsDirectlyLaunched() {
await metric_properties.set(LAUNCHED_PROPERTY, '');
}
/**
* Returns true when the extension is lauched for the first time
*/
export async function isFirstDirectLaunch(): Promise<boolean> {
return !(await metric_properties.exists(LAUNCHED_PROPERTY));
}
}
export namespace internal {
/**
* Returns the opt-in level of enhanced analytics.
*/
export async function getOptInLevel(): Promise<number> {
const levelStr = await metric_properties.get(INTERNAL_PROPERTY);
if (levelStr === undefined) {
if (await external.isEnabled()) {
return 1;
} else {
return 0;
}
}
const level = parseInt(levelStr);
if (level >= 0 && level <= 2) {
return level;
}
return 0;
}
/**
* Returns true if the user is new
*/
export async function isNewUser(): Promise<boolean> {
return (! await metric_properties.exists(ENABLED_PROPERTY)) &&
(! await metric_properties.exists(INTERNAL_PROPERTY));
}
/**
* Returns true if the user has enabled external analytics but haven't opt-in/out for internal analytics
*/
export async function isExistingUnmigratedOptedInUser(): Promise<boolean> {
return (! await metric_properties.exists(INTERNAL_PROPERTY)) && await external.isEnabled();
}
/**
* Returns true if the user has disabled external analytics but haven't opt-in/out for internal analytics
*/
export async function isExistingUnmigratedOptedOutUser(): Promise<boolean> {
return (! await metric_properties.exists(INTERNAL_PROPERTY)) && (! await external.isEnabled());
}
/**
* Opt out internal analytics
*/
export async function optOut(): Promise<void> {
await metric_properties.set(INTERNAL_PROPERTY, '0');
}
}
/**
* Returns the user's UUID if it exists. Otherwise returns undefined.
*/
export async function getUuid(): Promise<string | undefined> {
return await metric_properties.get(UUID_PROPERTY);
}
export class EnabledPropertyWatcher {
private readonly watcher: vscode.FileSystemWatcher;
constructor() {
const pattern =
new vscode.RelativePattern(metric_properties.getMetricDirectory(), ENABLED_PROPERTY);
this.watcher = vscode.workspace.createFileSystemWatcher(pattern);
}
onDidChange(listener: (e: vscode.Uri) => any) {
return this.watcher.onDidChange(listener);
}
dispose() {
this.watcher.dispose();
}
}
export class InternalPropertyWatcher {
private readonly watcher: vscode.FileSystemWatcher;
constructor() {
const pattern =
new vscode.RelativePattern(metric_properties.getMetricDirectory(), INTERNAL_PROPERTY);
this.watcher = vscode.workspace.createFileSystemWatcher(pattern);
}
onDidChange(listener: (e: vscode.Uri) => any) {
return this.watcher.onDidChange(listener);
}
dispose() {
this.watcher.dispose();
}
}