mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
Adding more validators for counts
This commit is contained in:
@@ -51,6 +51,30 @@ const itemUpdated$: Observable<UserActionEvent> = of({
|
||||
tags: ["with-folder"],
|
||||
});
|
||||
|
||||
const itemDeleted$: Observable<UserActionEvent> = of({
|
||||
"@timestamp": Date.now(),
|
||||
user: {
|
||||
id: "08f95669-1a65-4840-ba8b-3538cb3e1496" as UserId,
|
||||
},
|
||||
event: {
|
||||
kind: "event",
|
||||
category: "session",
|
||||
type: "deletion",
|
||||
outcome: "success",
|
||||
provider: "vault",
|
||||
},
|
||||
service: {
|
||||
name: "extension",
|
||||
type: "client",
|
||||
node: { name: "commotion-amused-rinse-trivial-sadly" },
|
||||
environment: "production",
|
||||
version: "2025.3.1-innovation-sprint",
|
||||
},
|
||||
action: "vault-item-removed",
|
||||
labels: { "vault-item-type": "login" },
|
||||
tags: [],
|
||||
});
|
||||
|
||||
const itemMovedToCollection$: Observable<UserActionEvent> = of(
|
||||
{
|
||||
"@timestamp": Date.now(),
|
||||
@@ -100,4 +124,4 @@ const itemMovedToCollection$: Observable<UserActionEvent> = of(
|
||||
} satisfies UserActionEvent,
|
||||
);
|
||||
|
||||
export { itemAdded$, itemUpdated$, itemMovedToCollection$ };
|
||||
export { itemAdded$, itemUpdated$, itemDeleted$, itemMovedToCollection$ };
|
||||
|
||||
@@ -65,22 +65,16 @@ export class ItemCreatedCountConfig implements Achievement {
|
||||
1,
|
||||
CipherType.Card,
|
||||
);
|
||||
static readonly CardItemCreated10 = new ItemCreatedCountConfig(
|
||||
"card-item-created-ten",
|
||||
"10 card items added",
|
||||
10,
|
||||
static readonly CardItemCreated3 = new ItemCreatedCountConfig(
|
||||
"card-item-created-3",
|
||||
"3rd card items added",
|
||||
3,
|
||||
CipherType.Card,
|
||||
);
|
||||
static readonly CardItemCreated50 = new ItemCreatedCountConfig(
|
||||
"card-item-created-fifty",
|
||||
"50 card items added",
|
||||
50,
|
||||
CipherType.Card,
|
||||
);
|
||||
static readonly CardItemCreated100 = new ItemCreatedCountConfig(
|
||||
"card-item-created-one-hundred",
|
||||
"100 card items added",
|
||||
100,
|
||||
static readonly CardItemCreated5 = new ItemCreatedCountConfig(
|
||||
"card-item-created-5",
|
||||
"5th card item added",
|
||||
5,
|
||||
CipherType.Card,
|
||||
);
|
||||
|
||||
@@ -110,6 +104,13 @@ export class ItemCreatedCountConfig implements Achievement {
|
||||
CipherType.SecureNote,
|
||||
);
|
||||
|
||||
// SSH Key - Achievements indicate only one so just set threshold at 1
|
||||
static readonly SSHKeyItemCreated = new ItemCreatedCountConfig(
|
||||
"ssh-key-item-created",
|
||||
"1st SSH Key added",
|
||||
1,
|
||||
);
|
||||
|
||||
base: Achievement;
|
||||
get achievement() {
|
||||
return this.base.achievement;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Type } from "../../data";
|
||||
import { Achievement, AchievementId, MetricId } from "../../types";
|
||||
|
||||
/**
|
||||
* Send items added in it's own achievement. Right now only count achievements
|
||||
* are included. There might be the need to create count achievements for
|
||||
* different send types. Keeping send logic in it's own validator makes sense
|
||||
* for readability.
|
||||
*/
|
||||
export class SendItemCreatedCountConfig implements Achievement {
|
||||
// Define send count achievements here
|
||||
static readonly SendItemCreated = new SendItemCreatedCountConfig(
|
||||
"send-item-created",
|
||||
"1st send item created",
|
||||
1,
|
||||
);
|
||||
static readonly SendItemCreated10 = new SendItemCreatedCountConfig(
|
||||
"send-item-created-10",
|
||||
"10 send items created",
|
||||
10,
|
||||
);
|
||||
static readonly SendItemCreated50 = new SendItemCreatedCountConfig(
|
||||
"send-item-created-50",
|
||||
"50 send items created",
|
||||
50,
|
||||
);
|
||||
static readonly SendItemCreated100 = new SendItemCreatedCountConfig(
|
||||
"send-item-created-100",
|
||||
"100 send items created",
|
||||
100,
|
||||
);
|
||||
|
||||
base: Achievement;
|
||||
get achievement() {
|
||||
return this.base.achievement;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.base.name;
|
||||
}
|
||||
|
||||
get validator() {
|
||||
return Type.Threshold;
|
||||
}
|
||||
|
||||
get active() {
|
||||
return this.base.active;
|
||||
}
|
||||
|
||||
get hidden() {
|
||||
return false;
|
||||
}
|
||||
threshold: number;
|
||||
private constructor(key: string, name: string, threshold: number) {
|
||||
this.threshold = threshold;
|
||||
this.base.achievement = key as AchievementId;
|
||||
this.base.name = name;
|
||||
this.base.active = {
|
||||
metric: "send-item-quantity" as MetricId,
|
||||
low: threshold - 1,
|
||||
high: threshold,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { earnedEvent } from "../achievement-events";
|
||||
import { Type } from "../data";
|
||||
import {
|
||||
AchievementId,
|
||||
AchievementProgressEvent,
|
||||
AchievementValidator,
|
||||
UserActionEvent,
|
||||
} from "../types";
|
||||
|
||||
export class ItemCollectionMoveValidator implements AchievementValidator {
|
||||
base: AchievementValidator;
|
||||
get achievement() {
|
||||
return "item-collection-move" as AchievementId;
|
||||
}
|
||||
get name() {
|
||||
return "1st item moved to a collection";
|
||||
}
|
||||
get validator() {
|
||||
return Type.HasTag;
|
||||
}
|
||||
get active() {
|
||||
return this.base.active;
|
||||
}
|
||||
get hidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.base.active = "until-earned";
|
||||
}
|
||||
|
||||
trigger(item: UserActionEvent) {
|
||||
// This achievement is specific to moving an item into a collection.
|
||||
// If there is ever an achievement for creating a vault item in a collection
|
||||
// this class can be renamed and reused to define a move or create achievement.
|
||||
return item.action === "vault-item-moved" && (item.tags?.includes("collection") ?? false);
|
||||
}
|
||||
|
||||
award(_measured: AchievementProgressEvent[]) {
|
||||
return [earnedEvent(this.achievement)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { earnedEvent, progressEvent } from "../achievement-events";
|
||||
import { Type } from "../data";
|
||||
import {
|
||||
AchievementId,
|
||||
AchievementProgressEvent,
|
||||
AchievementValidator,
|
||||
MetricId,
|
||||
UserActionEvent,
|
||||
} from "../types";
|
||||
|
||||
export class ItemRemovedValidator implements AchievementValidator {
|
||||
base: AchievementValidator;
|
||||
get achievement() {
|
||||
return "item-removed" as AchievementId;
|
||||
}
|
||||
get name() {
|
||||
return "1st item removed from vault";
|
||||
}
|
||||
// Threshold validator because we are only looking
|
||||
// for the action of removed and the threshold is 1
|
||||
get validator() {
|
||||
return Type.Threshold;
|
||||
}
|
||||
get active() {
|
||||
return this.base.active;
|
||||
}
|
||||
get hidden() {
|
||||
return false;
|
||||
}
|
||||
private metric = "item-removed-quantity" as MetricId;
|
||||
constructor() {
|
||||
this.base.active = {
|
||||
metric: this.metric,
|
||||
high: 1,
|
||||
};
|
||||
}
|
||||
|
||||
trigger(item: UserActionEvent) {
|
||||
return item.action === "vault-item-removed";
|
||||
}
|
||||
|
||||
measure(item: UserActionEvent, metrics: Map<MetricId, number>) {
|
||||
return [progressEvent(this.metric)];
|
||||
}
|
||||
|
||||
award(_measured: AchievementProgressEvent[]) {
|
||||
return [earnedEvent(this.achievement)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { earnedEvent } from "../achievement-events";
|
||||
import { Type } from "../data";
|
||||
import {
|
||||
AchievementId,
|
||||
AchievementProgressEvent,
|
||||
AchievementValidator,
|
||||
UserActionEvent,
|
||||
} from "../types";
|
||||
|
||||
export class ItemUriAddedValidator implements AchievementValidator {
|
||||
base: AchievementValidator;
|
||||
get achievement() {
|
||||
return "item-uri-added" as AchievementId;
|
||||
}
|
||||
get name() {
|
||||
return "1st time adding a uri to an item";
|
||||
}
|
||||
get validator() {
|
||||
return Type.Threshold;
|
||||
}
|
||||
get active() {
|
||||
return this.base.active;
|
||||
}
|
||||
get hidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
// If edit and the uri count is present the achievement is rewarded
|
||||
this.base.active = "until-earned";
|
||||
}
|
||||
|
||||
trigger(item: UserActionEvent) {
|
||||
// The achievement states that the uri needs to be added
|
||||
// to vault item. Indicating an edit/update
|
||||
return (
|
||||
item.action === "vault-item-updated" &&
|
||||
Number.isNaN(item.labels?.["vault-item-uri-quantity"]) &&
|
||||
(item.labels?.["vault-item-uri-quantity"] as number) >= 1
|
||||
);
|
||||
}
|
||||
|
||||
award(_measured: AchievementProgressEvent[]) {
|
||||
return [earnedEvent(this.achievement)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { earnedEvent, progressEvent } from "../achievement-events";
|
||||
import {
|
||||
AchievementProgressEvent,
|
||||
AchievementValidator,
|
||||
MetricId,
|
||||
UserActionEvent,
|
||||
} from "../types";
|
||||
|
||||
import { SendItemCreatedCountConfig } from "./config/send-created-count-config";
|
||||
|
||||
export class SendItemCreatedCountValidator implements AchievementValidator {
|
||||
private sendItemCreatedProgress: MetricId;
|
||||
constructor(private config: SendItemCreatedCountConfig) {
|
||||
// All of the configs for created items must have a metric.
|
||||
// This checks the types to allow us to assign the metric.
|
||||
if (config.active === "until-earned") {
|
||||
throw new Error(
|
||||
`${config.achievement}: invalid configuration; 'active' must contain a metric`,
|
||||
);
|
||||
}
|
||||
|
||||
this.sendItemCreatedProgress = config.active.metric;
|
||||
}
|
||||
|
||||
base: AchievementValidator;
|
||||
get achievement() {
|
||||
return this.config.achievement;
|
||||
}
|
||||
get name() {
|
||||
return this.config.name;
|
||||
}
|
||||
get validator() {
|
||||
return this.config.validator;
|
||||
}
|
||||
get active() {
|
||||
return this.config.active;
|
||||
}
|
||||
get hidden() {
|
||||
return this.config.hidden;
|
||||
}
|
||||
|
||||
trigger(item: UserActionEvent) {
|
||||
return item.action === "send-item-added";
|
||||
}
|
||||
|
||||
measure(_item: UserActionEvent, progress: Map<MetricId, number>) {
|
||||
const value = 1 + (progress.get(this.sendItemCreatedProgress) ?? 0);
|
||||
return [progressEvent(this.sendItemCreatedProgress, value)];
|
||||
}
|
||||
|
||||
award(_measured: AchievementProgressEvent[], progress: Map<MetricId, number>) {
|
||||
const value = progress.get(this.sendItemCreatedProgress) ?? 0;
|
||||
return value >= this.config.threshold ? [earnedEvent(this.achievement)] : [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user