123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- 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", "both", "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(),
- 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(),
- value: Joi.object().keys({
- newtab_url_category: Joi.string(),
- newtab_extension_id: Joi.string(),
- home_url_category: Joi.string(),
- home_extension_id: 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",
- "SEARCH_HANDOFF",
- "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", "search"]),
- search_vendor: Joi.valid(["google", "amazon"]),
- has_flow_params: Joi.bool(),
- }),
- }).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 SpocsFillEntrySchema = Joi.object().keys({
- id: Joi.number().integer().required(),
- displayed: Joi.number().integer().required(),
- reason: Joi.string().required(),
- full_recalc: Joi.number().integer().required(),
- });
- export const SpocsFillPing = Joi.object().keys(Object.assign({}, baseKeys, {
- impression_id: Joi.string().required(),
- client_id: Joi.valid("n/a").required(),
- session_id: Joi.valid("n/a").required(),
- spoc_fills: Joi.array().items(SpocsFillEntrySchema).required(),
- }));
- 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(),
- // The count of search shortcut Top Sites.
- topsites_search_shortcuts: 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(),
- }).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 const trailheadEnrollExtraKeys = Joi.object().keys({
- experimentType: Joi.string().required(),
- branch: Joi.string().required(),
- }).options({allowUnknown: false});
- export const UTTrailheadEnrollPing = Joi.array().items(
- Joi.string().required().valid("activity_stream"),
- Joi.string().required().valid("enroll"),
- Joi.string().required().valid("preference_study"),
- Joi.string().required(),
- trailheadEnrollExtraKeys
- );
- 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);
- }
|