Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1683057 - Implement the UUID metric type #17

Merged
merged 5 commits into from
Dec 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@types/assert": "^1.5.2",
"@types/mocha": "^8.0.3",
"@types/selenium-webdriver": "^4.0.10",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^4.6.1",
"@typescript-eslint/parser": "^4.6.1",
"eslint": "^7.12.1",
Expand All @@ -49,5 +50,8 @@
"web-ext-types": "^3.2.1",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0"
},
"dependencies": {
"uuid": "^8.3.2"
}
}
2 changes: 1 addition & 1 deletion src/glean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class Glean {
*
* TODO: Only allow this function to be called on test mode (depends on Bug 1682771).
*/
static async resetGlean(): Promise<void> {
static async testRestGlean(): Promise<void> {
// Reset upload enabled state, not to inerfere with other tests.
Glean.uploadEnabled = true;
// Clear the database.
Expand Down
2 changes: 1 addition & 1 deletion src/metrics/boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class BooleanMetric extends Metric {
*
* @param value the value to set.
*/
async set(value: BooleanMetricPayload): Promise<void> {
async set(value: boolean): Promise<void> {
if (!this.shouldRecord()) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/metrics/counter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class CounterMetric extends Metric {

if (amount <= 0) {
// TODO: record error once Bug 1682574 is resolved.
console.warn(`Attempted to add an invalid amount ${amount}. `);
console.warn(`Attempted to add an invalid amount ${amount}. Ignoring.`);
return;
}

Expand Down
6 changes: 5 additions & 1 deletion src/metrics/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { BooleanMetricPayload, isBooleanMetricPayload } from "metrics/boolean";
import { CounterMetricPayload, isCounterMetricPayload } from "metrics/counter";
import { StringMetricPayload, isStringMetricPayload } from "metrics/string";
import { UUIDMetricPayload, isUUIDMetricPayload } from "metrics/uuid";

/**
* Validates that a given value is the correct type of payload for a metric of a given type.
Expand All @@ -22,6 +23,8 @@ export function isMetricPayload<T>(type: string, v: unknown): v is T {
return isCounterMetricPayload(v);
case "string":
return isStringMetricPayload(v);
case "uuid":
return isUUIDMetricPayload(v);
default:
return false;
}
Expand All @@ -31,5 +34,6 @@ export function isMetricPayload<T>(type: string, v: unknown): v is T {
export type MetricPayload =
BooleanMetricPayload |
CounterMetricPayload |
StringMetricPayload;
StringMetricPayload |
UUIDMetricPayload;

98 changes: 98 additions & 0 deletions src/metrics/uuid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { v4 as UUIDv4, validate as UUIDvalidate } from "uuid";

import Metric, { CommonMetricData } from "metrics";
import Glean from "glean";
import { isString } from "utils";

export type UUIDMetricPayload = string;

/**
* Checks whether or not `v` is a valid UUID metric payload.
*
* @param v The value to verify.
*
* @returns A special Typescript value (which compiles down to a boolean)
* stating whether `v` is a valid boolean metric payload.
*/
export function isUUIDMetricPayload(v: unknown): v is UUIDMetricPayload {
if (!isString(v)) {
return false;
}

return UUIDvalidate(v);
}

/**
* An UUID metric.
*
* Stores UUID v4 (randomly generated) values.
*/
class UUIDMetric extends Metric {
constructor(meta: CommonMetricData) {
super("uuid", meta);
}

/**
* Sets to the specified value.
*
* @param value the value to set.
*
* @throws In case `value` is not a valid UUID.
*/
async set(value: string): Promise<void> {
if (!this.shouldRecord()) {
return;
}

if (!value) {
value = UUIDv4();
}

if (!isUUIDMetricPayload(value)) {
// TODO: record error once Bug 1682574 is resolved.
console.warn(`"${value}" is not a valid UUID. Ignoring`);
return;
}

await Glean.db.record(this, value);
}

/**
* Generates a new random uuid and sets the metric to it.
*
* @returns The generated value or `undefined` in case this metric shouldn't be recorded.
*/
async generateAndSet(): Promise<UUIDMetricPayload | undefined> {
if (!this.shouldRecord()) {
return;
}

const value = UUIDv4();
await this.set(value);

return value;
}

/**
* **Test-only API**
*
* Gets the currently stored value as a string.
*
* This doesn't clear the stored value.
*
* TODO: Only allow this function to be called on test mode (depends on Bug 1682771).
*
* @param ping the ping from which we want to retrieve this metrics value from.
*
* @returns The value found in storage or `undefined` if nothing was found.
*/
async testGetValue(ping: string): Promise<UUIDMetricPayload | undefined> {
return Glean.db.getMetric(ping, this);
}
}

export default UUIDMetric;
2 changes: 1 addition & 1 deletion tests/metrics/boolean.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Lifetime } from "metrics";

describe("BooleanMetric", function() {
beforeEach(async function() {
await Glean.resetGlean();
await Glean.testRestGlean();
});

it("attemping to get the value of a metric that hasn't been recorded doesn't error", async function() {
Expand Down
2 changes: 1 addition & 1 deletion tests/metrics/counter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Lifetime } from "metrics";

describe("CounterMetric", function() {
beforeEach(async function() {
await Glean.resetGlean();
await Glean.testRestGlean();
});

it("attemping to get the value of a metric that hasn't been recorded doesn't error", async function() {
Expand Down
2 changes: 1 addition & 1 deletion tests/metrics/string.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Lifetime } from "metrics";

describe("StringMetric", function() {
beforeEach(async function() {
await Glean.resetGlean();
await Glean.testRestGlean();
});

it("attemping to get the value of a metric that hasn't been recorded doesn't error", async function() {
Expand Down
108 changes: 108 additions & 0 deletions tests/metrics/uuid.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import assert from "assert";
import { v4 as UUIDv4 } from "uuid";

import Glean from "glean";
import UUIDMetric, { isUUIDMetricPayload } from "metrics/uuid";
import { Lifetime } from "metrics";

describe("UUIDMetric", function() {
beforeEach(async function() {
await Glean.testRestGlean();
});

it("attemping to get the value of a metric that hasn't been recorded doesn't error", async function() {
const metric = new UUIDMetric({
category: "aCategory",
name: "aUUIDMetric",
sendInPings: ["aPing", "twoPing", "threePing"],
lifetime: Lifetime.Ping,
disabled: false
});

assert.strictEqual(await metric.testGetValue("aPing"), undefined);
});

it("attemping to set when glean upload is disabled is a no-op", async function() {
Glean.uploadEnabled = false;

const metric = new UUIDMetric({
category: "aCategory",
name: "aUUIDMetric",
sendInPings: ["aPing", "twoPing", "threePing"],
lifetime: Lifetime.Ping,
disabled: false
});

await metric.generateAndSet();
assert.strictEqual(await metric.testGetValue("aPing"), undefined);
});

it("attemping to set an invalid uuid is a no-op", async function() {
Glean.uploadEnabled = false;

const metric = new UUIDMetric({
category: "aCategory",
name: "aUUIDMetric",
sendInPings: ["aPing", "twoPing", "threePing"],
lifetime: Lifetime.Ping,
disabled: false
});

await metric.set("not valid");
assert.strictEqual(await metric.testGetValue("aPing"), undefined);
});

it("ping payload is correct", async function() {
const metric = new UUIDMetric({
category: "aCategory",
name: "aUUIDMetric",
sendInPings: ["aPing"],
lifetime: Lifetime.Ping,
disabled: false
});

const expected = UUIDv4();
await metric.set(expected);
assert.strictEqual(await metric.testGetValue("aPing"), expected);

const snapshot = await Glean.db.getPing("aPing", true);
assert.deepStrictEqual(snapshot, {
"uuid": {
"aCategory.aUUIDMetric": expected
}
});
});

it("set properly sets the value in all pings", async function() {
const metric = new UUIDMetric({
category: "aCategory",
name: "aUUIDMetric",
sendInPings: ["aPing", "twoPing", "threePing"],
lifetime: Lifetime.Ping,
disabled: false
});

const expected = UUIDv4();
await metric.set(expected);
assert.strictEqual(await metric.testGetValue("aPing"), expected);
assert.strictEqual(await metric.testGetValue("twoPing"), expected);
assert.strictEqual(await metric.testGetValue("threePing"), expected);
});

it("uuid is generated and stored", async function() {
const metric = new UUIDMetric({
category: "aCategory",
name: "aUUIDMetric",
sendInPings: ["aPing"],
lifetime: Lifetime.Ping,
disabled: false
});

await metric.generateAndSet();
assert(isUUIDMetricPayload(await metric.testGetValue("aPing")));
});
});