"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 : ""}`); } // 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