258 lines
8.3 KiB
JavaScript
258 lines
8.3 KiB
JavaScript
import {CONTENT_MESSAGE_TYPE, MAIN_MESSAGE_TYPE} from "common/Actions.jsm";
|
|
import Joi from "joi-browser";
|
|
|
|
export const baseKeys = {
|
|
// client_id will be set by PingCentre if it doesn't exist.
|
|
client_id: Joi.string().optional(),
|
|
addon_version: Joi.string().required(),
|
|
locale: Joi.string().required(),
|
|
session_id: Joi.string(),
|
|
page: Joi.valid(["about:home", "about:newtab", "about:welcome", "unknown"]),
|
|
user_prefs: Joi.number().integer().required()
|
|
};
|
|
|
|
export const BasePing = Joi.object().keys(baseKeys).options({allowUnknown: true});
|
|
|
|
export const eventsTelemetryExtraKeys = Joi.object().keys({
|
|
session_id: baseKeys.session_id.required(),
|
|
page: baseKeys.page.required(),
|
|
addon_version: baseKeys.addon_version.required(),
|
|
user_prefs: baseKeys.user_prefs.required(),
|
|
action_position: Joi.string().optional()
|
|
}).options({allowUnknown: false});
|
|
|
|
export const UserEventPing = Joi.object().keys(Object.assign({}, baseKeys, {
|
|
session_id: baseKeys.session_id.required(),
|
|
page: baseKeys.page.required(),
|
|
source: Joi.string().required(),
|
|
event: Joi.string().required(),
|
|
action: Joi.valid("activity_stream_user_event").required(),
|
|
metadata_source: Joi.string(),
|
|
highlight_type: Joi.valid(["bookmarks", "recommendation", "history"]),
|
|
recommender_type: Joi.string()
|
|
}));
|
|
|
|
export const UTUserEventPing = Joi.array().items(
|
|
Joi.string().required().valid("activity_stream"),
|
|
Joi.string().required().valid("event"),
|
|
Joi.string().required().valid([
|
|
"CLICK",
|
|
"SEARCH",
|
|
"BLOCK",
|
|
"DELETE",
|
|
"DELETE_CONFIRM",
|
|
"DIALOG_CANCEL",
|
|
"DIALOG_OPEN",
|
|
"OPEN_NEW_WINDOW",
|
|
"OPEN_PRIVATE_WINDOW",
|
|
"OPEN_NEWTAB_PREFS",
|
|
"CLOSE_NEWTAB_PREFS",
|
|
"BOOKMARK_DELETE",
|
|
"BOOKMARK_ADD",
|
|
"PIN",
|
|
"UNPIN",
|
|
"SAVE_TO_POCKET"
|
|
]),
|
|
Joi.string().required(),
|
|
eventsTelemetryExtraKeys
|
|
);
|
|
|
|
// Use this to validate actions generated from Redux
|
|
export const UserEventAction = Joi.object().keys({
|
|
type: Joi.string().required(),
|
|
data: Joi.object().keys({
|
|
event: Joi.valid([
|
|
"CLICK",
|
|
"SEARCH",
|
|
"BLOCK",
|
|
"DELETE",
|
|
"DELETE_CONFIRM",
|
|
"DIALOG_CANCEL",
|
|
"DIALOG_OPEN",
|
|
"OPEN_NEW_WINDOW",
|
|
"OPEN_PRIVATE_WINDOW",
|
|
"OPEN_NEWTAB_PREFS",
|
|
"CLOSE_NEWTAB_PREFS",
|
|
"BOOKMARK_DELETE",
|
|
"BOOKMARK_ADD",
|
|
"PIN",
|
|
"PREVIEW_REQUEST",
|
|
"UNPIN",
|
|
"SAVE_TO_POCKET",
|
|
"MENU_MOVE_UP",
|
|
"MENU_MOVE_DOWN",
|
|
"SCREENSHOT_REQUEST",
|
|
"MENU_REMOVE",
|
|
"MENU_COLLAPSE",
|
|
"MENU_EXPAND",
|
|
"MENU_MANAGE",
|
|
"MENU_ADD_TOPSITE",
|
|
"MENU_PRIVACY_NOTICE",
|
|
"DELETE_FROM_POCKET",
|
|
"ARCHIVE_FROM_POCKET",
|
|
"SKIPPED_SIGNIN",
|
|
"SUBMIT_EMAIL"
|
|
]).required(),
|
|
source: Joi.valid(["TOP_SITES", "TOP_STORIES", "HIGHLIGHTS"]),
|
|
action_position: Joi.number().integer(),
|
|
value: Joi.object().keys({
|
|
icon_type: Joi.valid(["tippytop", "rich_icon", "screenshot_with_icon", "screenshot", "no_image"]),
|
|
card_type: Joi.valid(["bookmark", "trending", "pinned", "pocket"])
|
|
})
|
|
}).required(),
|
|
meta: Joi.object().keys({
|
|
to: Joi.valid(MAIN_MESSAGE_TYPE).required(),
|
|
from: Joi.valid(CONTENT_MESSAGE_TYPE).required()
|
|
}).required()
|
|
});
|
|
|
|
export const UndesiredPing = Joi.object().keys(Object.assign({}, baseKeys, {
|
|
source: Joi.string().required(),
|
|
event: Joi.string().required(),
|
|
action: Joi.valid("activity_stream_undesired_event").required(),
|
|
value: Joi.number().required()
|
|
}));
|
|
|
|
export const TileSchema = Joi.object().keys({
|
|
id: Joi.number().integer().required(),
|
|
pos: Joi.number().integer()
|
|
});
|
|
|
|
export const ImpressionStatsPing = Joi.object().keys(Object.assign({}, baseKeys, {
|
|
source: Joi.string().required(),
|
|
impression_id: Joi.string().required(),
|
|
client_id: Joi.valid("n/a").required(),
|
|
session_id: Joi.valid("n/a").required(),
|
|
action: Joi.valid("activity_stream_impression_stats").required(),
|
|
tiles: Joi.array().items(TileSchema).required(),
|
|
click: Joi.number().integer(),
|
|
block: Joi.number().integer(),
|
|
pocket: Joi.number().integer()
|
|
}));
|
|
|
|
export const PerfPing = Joi.object().keys(Object.assign({}, baseKeys, {
|
|
source: Joi.string(),
|
|
event: Joi.string().required(),
|
|
action: Joi.valid("activity_stream_performance_event").required(),
|
|
value: Joi.number().required()
|
|
}));
|
|
|
|
export const SessionPing = Joi.object().keys(Object.assign({}, baseKeys, {
|
|
session_id: baseKeys.session_id.required(),
|
|
page: baseKeys.page.required(),
|
|
session_duration: Joi.number().integer(),
|
|
action: Joi.valid("activity_stream_session").required(),
|
|
perf: Joi.object().keys({
|
|
// How long it took in ms for data to be ready for display.
|
|
highlights_data_late_by_ms: Joi.number().positive(),
|
|
|
|
// Timestamp of the action perceived by the user to trigger the load
|
|
// of this page.
|
|
//
|
|
// Not required at least for the error cases where the
|
|
// observer event doesn't fire
|
|
load_trigger_ts: Joi.number().positive()
|
|
.notes(["server counter", "server counter alert"]),
|
|
|
|
// What was the perceived trigger of the load action?
|
|
//
|
|
// Not required at least for the error cases where the observer event
|
|
// doesn't fire
|
|
load_trigger_type: Joi.valid(["first_window_opened",
|
|
"menu_plus_or_keyboard", "unexpected"])
|
|
.notes(["server counter", "server counter alert"]).required(),
|
|
|
|
// How long it took in ms for data to be ready for display.
|
|
topsites_data_late_by_ms: Joi.number().positive(),
|
|
|
|
// When did the topsites element finish painting? Note that, at least for
|
|
// the first tab to be loaded, and maybe some others, this will be before
|
|
// topsites has yet to receive screenshots updates from the add-on code,
|
|
// and is therefore just showing placeholder screenshots.
|
|
topsites_first_painted_ts: Joi.number().positive()
|
|
.notes(["server counter", "server counter alert"]),
|
|
|
|
// Information about the quality of TopSites images and icons.
|
|
topsites_icon_stats: Joi.object().keys({
|
|
custom_screenshot: Joi.number(),
|
|
rich_icon: Joi.number(),
|
|
screenshot: Joi.number(),
|
|
screenshot_with_icon: Joi.number(),
|
|
tippytop: Joi.number(),
|
|
no_image: Joi.number()
|
|
}),
|
|
|
|
// The count of pinned Top Sites.
|
|
topsites_pinned: Joi.number(),
|
|
|
|
// When the page itself receives an event that document.visibilityState
|
|
// == visible.
|
|
//
|
|
// Not required at least for the (error?) case where the
|
|
// visibility_event doesn't fire. (It's not clear whether this
|
|
// can happen in practice, but if it does, we'd like to know about it).
|
|
visibility_event_rcvd_ts: Joi.number().positive()
|
|
.notes(["server counter", "server counter alert"]),
|
|
|
|
// The boolean to signify whether the page is preloaded or not.
|
|
is_preloaded: Joi.bool().required(),
|
|
|
|
// The boolean to signify whether the page is prerendered or not.
|
|
is_prerendered: Joi.bool().required()
|
|
}).required()
|
|
}));
|
|
|
|
export const ASRouterEventPing = Joi.object().keys({
|
|
client_id: Joi.string().required(),
|
|
action: Joi.string().required(),
|
|
impression_id: Joi.string().required(),
|
|
source: Joi.string().required(),
|
|
addon_version: Joi.string().required(),
|
|
locale: Joi.string().required(),
|
|
message_id: Joi.string().required(),
|
|
event: Joi.string().required()
|
|
});
|
|
|
|
export const UTSessionPing = Joi.array().items(
|
|
Joi.string().required().valid("activity_stream"),
|
|
Joi.string().required().valid("end"),
|
|
Joi.string().required().valid("session"),
|
|
Joi.string().required(),
|
|
eventsTelemetryExtraKeys
|
|
);
|
|
|
|
export function chaiAssertions(_chai, utils) {
|
|
const {Assertion} = _chai;
|
|
|
|
Assertion.addMethod("validate", function(schema, schemaName) {
|
|
const {error} = Joi.validate(this._obj, schema, {allowUnknown: false});
|
|
this.assert(
|
|
!error,
|
|
`Expected to be ${schemaName ? `a valid ${schemaName}` : "valid"} but there were errors: ${error}`
|
|
);
|
|
});
|
|
|
|
const assertions = {
|
|
/**
|
|
* assert.validate - Validates an item given a Joi schema
|
|
*
|
|
* @param {any} actual The item to validate
|
|
* @param {obj} schema A Joi schema
|
|
*/
|
|
validate(actual, schema, schemaName) {
|
|
new Assertion(actual).validate(schema, schemaName);
|
|
},
|
|
|
|
/**
|
|
* isUserEventAction - Passes if the item is a valid UserEvent action
|
|
*
|
|
* @param {any} actual The item to validate
|
|
*/
|
|
isUserEventAction(actual) {
|
|
new Assertion(actual).validate(UserEventAction, "UserEventAction");
|
|
}
|
|
};
|
|
|
|
Object.assign(_chai.assert, assertions);
|
|
}
|