Files
2025-11-04 05:03:50 +08:00

155 lines
6.7 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventLinter = void 0;
const object_reader_1 = require("../json/reader/object-reader");
const optional_1 = require("../types/optional");
const numerics = require("./helpers/numerics");
/**
* A type which applies common business rules to metrics fields
* and generates events which are ready for posting to Figaro.
*
* The common business rules are:
* - Add base event fields provided by linter configuration provider object
* - Set clientBuiltType and resourceRevNum fields based on EventLinterEnvironment object values
* - Set xpSendMethod field to "jet-js"
* - Combine pageType and pageId using compound separator provided by linter configuration provider
* and set result to "page" field.
* - Apply event field de-resolution rules provided by linter configuration provider object
* - Set the "position" field for events of "media" type.
*/
class EventLinter {
/**
* Create an event linter.
*
* @param options - The options which specify various behaviors of the new linter.
* This object will be frozen.
*/
constructor(options) {
this.options = Object.freeze(options);
}
// MARK: Public Properties
/**
* Topic to use if an event fields blob does not specify one.
*/
get defaultTopic() {
return this.options.defaultTopic;
}
// MARK: Utilities
/**
* Reduce the accuracy of fields in a blob according to
* a given array of rules provided by linter configuration object.
*
* @param eventFields - The fields of an event to reduce the accuracy of.
* @param rules - An array of de-resolution rules to apply to event fields.
*/
applyDeResolutionRules(eventFields, rules) {
const eventFieldsReader = new object_reader_1.ObjectReader(eventFields);
for (const rule of rules) {
const value = eventFieldsReader.asNumber(rule.fieldName);
if ((0, optional_1.isNothing)(value)) {
continue;
}
let magnitude = rule.magnitude;
if ((0, optional_1.isNothing)(magnitude)) {
magnitude = 1024 * 1024;
}
let significantDigits = rule.significantDigits;
if ((0, optional_1.isNothing)(significantDigits)) {
significantDigits = 2;
}
if (magnitude <= 0.0 || significantDigits < 0.0) {
// This is the failure mode from MetricsKit.
eventFields[rule.fieldName] = Number.NaN;
continue;
}
const scaledValue = value / magnitude;
eventFields[rule.fieldName] = numerics.reduceSignificantDigits(scaledValue, significantDigits);
}
}
// MARK: Rules
/**
* Apply the rules which are universal to all metrics events
* to a given metrics fields linter.
*
* @param eventFields - The fields which will be used to construct a built event.
* @param topic - The topic the built event will be submitted to.
*/
decorateCommonEventFields(eventFields, topic) {
const eventFieldsReader = new object_reader_1.ObjectReader(eventFields);
const configurationProvider = this.options.configuration;
// - Base metrics fields.
const baseFields = configurationProvider.baseFields(topic);
if ((0, optional_1.isSome)(baseFields)) {
Object.assign(eventFields, baseFields);
}
// - Universal basic fields.
eventFields["clientBuildType"] = this.options.environment.buildType;
eventFields["resourceRevNum"] = this.options.environment.jsVersion;
eventFields["xpSendMethod"] = "jet-js";
// - Page.
const pageType = eventFieldsReader.asString("pageType");
const pageId = eventFieldsReader.asString("pageId");
if ((0, optional_1.isSome)(pageType) && (0, optional_1.isSome)(pageId) && (0, optional_1.isNothing)(eventFields["page"])) {
const bagValue = configurationProvider.compoundSeparator(topic);
const separator = (0, optional_1.isSome)(bagValue) ? (0, optional_1.unwrapOptional)(bagValue) : "_";
eventFields["page"] = `${pageType}${separator}${pageId}`;
}
// - Field value resolution reduction.
const rules = configurationProvider.deResolutionRules(topic);
this.applyDeResolutionRules(eventFields, rules);
}
/**
* Apply the rules specific to the `media` event.
*
* @param eventFields - The fields which will be used to construct a built event.
*/
decorateMediaEventEvents(eventFields) {
const eventFieldsReader = new object_reader_1.ObjectReader(eventFields);
const position = eventFieldsReader.asNumber("position");
if ((0, optional_1.isSome)(position)) {
eventFields["position"] = Math.round(position);
}
}
// MARK: Decorating Event Fields
/**
* Lint metrics event fields by applying the common business rules to a given fields blob.
*
* @remarks
*
* Note: A deep copy of event fields is created by linter using `JSON.parse(JSON.stringify(eventFields))`.
* The original event fields are not modified.
*
* @param eventFields - The fields to decorate.
* @param context - The additional event linter context to be passed to all
* event linter rules. This is a free-form object so clients can pass custom
* context information.
* @returns Decorated fields ready for creating a metrics event.
*/
lint(eventFields, context = {}) {
const eventFieldsReader = new object_reader_1.ObjectReader(eventFields);
const eventType = eventFieldsReader.asString("eventType");
if (this.options.isLoggingEnabled) {
console.log(`Building event for event type: ${eventType !== null && eventType !== void 0 ? eventType : "<null>"}`);
}
// Make sure we have a deep copy of an object.
const decoratedEventFields = JSON.parse(JSON.stringify(eventFields));
const value = eventFieldsReader.asString("topic");
const topic = (0, optional_1.isSome)(value) ? (0, optional_1.unwrapOptional)(value) : this.options.defaultTopic;
this.decorateCommonEventFields(decoratedEventFields, topic);
switch (eventType) {
case "media" /* MetricsEventType.media */:
this.decorateMediaEventEvents(decoratedEventFields);
break;
default:
break;
}
for (const rule of this.options.rules) {
rule.apply(decoratedEventFields, context);
}
return {
fields: decoratedEventFields,
};
}
}
exports.EventLinter = EventLinter;
//# sourceMappingURL=event-linter.js.map