1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
"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
|