@launchdarkly/js-server-sdk-common
Version:
LaunchDarkly Server SDK for JavaScript - common code
208 lines • 8.26 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const js_sdk_common_1 = require("@launchdarkly/js-sdk-common");
const data_1 = require("./api/data");
function isPopulated(data) {
return !Number.isNaN(data);
}
class MigrationOpTracker {
constructor(_flagKey, _context, _defaultStage, _stage, _reason, _checkRatio, _variation, _version, _samplingRatio, _logger) {
this._flagKey = _flagKey;
this._context = _context;
this._defaultStage = _defaultStage;
this._stage = _stage;
this._reason = _reason;
this._checkRatio = _checkRatio;
this._variation = _variation;
this._version = _version;
this._samplingRatio = _samplingRatio;
this._logger = _logger;
this._errors = {
old: false,
new: false,
};
this._wasInvoked = {
old: false,
new: false,
};
this._consistencyCheck = data_1.LDConsistencyCheck.NotChecked;
this._latencyMeasurement = {
old: NaN,
new: NaN,
};
}
op(op) {
this._operation = op;
}
error(origin) {
this._errors[origin] = true;
}
consistency(check) {
var _a, _b;
if (js_sdk_common_1.internal.shouldSample((_a = this._checkRatio) !== null && _a !== void 0 ? _a : 1)) {
try {
const res = check();
this._consistencyCheck = res
? data_1.LDConsistencyCheck.Consistent
: data_1.LDConsistencyCheck.Inconsistent;
}
catch (exception) {
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.error('Exception when executing consistency check function for migration' +
` '${this._flagKey}' the consistency check will not be included in the generated migration` +
` op event. Exception: ${exception}`);
}
}
}
latency(origin, value) {
this._latencyMeasurement[origin] = value;
}
invoked(origin) {
this._wasInvoked[origin] = true;
}
createEvent() {
var _a, _b, _c, _d, _e;
if (!js_sdk_common_1.TypeValidators.String.is(this._flagKey) || this._flagKey === '') {
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error('The flag key for a migration operation must be a non-empty string.');
return undefined;
}
if (!this._operation) {
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.error('The operation must be set using "op" before an event can be created.');
return undefined;
}
if (!js_sdk_common_1.Context.fromLDContext(this._context).valid) {
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.error('The migration was not done against a valid context and cannot generate an event.');
return undefined;
}
if (!this._wasInvoked.old && !this._wasInvoked.new) {
(_d = this._logger) === null || _d === void 0 ? void 0 : _d.error('The migration invoked neither the "old" or "new" implementation and' +
'an event cannot be generated');
return undefined;
}
if (!this._measurementConsistencyCheck()) {
return undefined;
}
const measurements = [];
this._populateInvoked(measurements);
this._populateConsistency(measurements);
this._populateLatency(measurements);
this._populateErrors(measurements);
return {
kind: 'migration_op',
operation: this._operation,
creationDate: Date.now(),
context: this._context,
evaluation: {
key: this._flagKey,
value: this._stage,
default: this._defaultStage,
reason: this._reason,
variation: this._variation,
version: this._version,
},
measurements,
samplingRatio: (_e = this._samplingRatio) !== null && _e !== void 0 ? _e : 1,
};
}
_logTag() {
return `For migration ${this._operation}-${this._flagKey}:`;
}
_latencyConsistencyMessage(origin) {
return `Latency measurement for "${origin}", but "${origin}" was not invoked.`;
}
_errorConsistencyMessage(origin) {
return `Error occurred for "${origin}", but "${origin}" was not invoked.`;
}
_consistencyCheckConsistencyMessage(origin) {
return (`Consistency check was done, but "${origin}" was not invoked.` +
'Both "old" and "new" must be invoked to do a consistency check.');
}
_checkOriginEventConsistency(origin) {
var _a, _b, _c;
if (this._wasInvoked[origin]) {
return true;
}
// If the specific origin was not invoked, but it contains measurements, then
// that is a problem. Check each measurement and log a message if it is present.
if (!Number.isNaN(this._latencyMeasurement[origin])) {
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error(`${this._logTag()} ${this._latencyConsistencyMessage(origin)}`);
return false;
}
if (this._errors[origin]) {
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.error(`${this._logTag()} ${this._errorConsistencyMessage(origin)}`);
return false;
}
if (this._consistencyCheck !== data_1.LDConsistencyCheck.NotChecked) {
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.error(`${this._logTag()} ${this._consistencyCheckConsistencyMessage(origin)}`);
return false;
}
return true;
}
/**
* Check that the latency, error, consistency and invoked measurements are self-consistent.
*/
_measurementConsistencyCheck() {
return this._checkOriginEventConsistency('old') && this._checkOriginEventConsistency('new');
}
_populateInvoked(measurements) {
var _a;
const measurement = {
key: 'invoked',
values: {},
};
if (!this._wasInvoked.old && !this._wasInvoked.new) {
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error('Migration op completed without executing any origins (old/new).');
}
if (this._wasInvoked.old) {
measurement.values.old = true;
}
if (this._wasInvoked.new) {
measurement.values.new = true;
}
measurements.push(measurement);
}
_populateConsistency(measurements) {
var _a;
if (this._consistencyCheck !== undefined &&
this._consistencyCheck !== data_1.LDConsistencyCheck.NotChecked) {
measurements.push({
key: 'consistent',
value: this._consistencyCheck === data_1.LDConsistencyCheck.Consistent,
samplingRatio: (_a = this._checkRatio) !== null && _a !== void 0 ? _a : 1,
});
}
}
_populateErrors(measurements) {
if (this._errors.new || this._errors.old) {
const measurement = {
key: 'error',
values: {},
};
if (this._errors.new) {
measurement.values.new = true;
}
if (this._errors.old) {
measurement.values.old = true;
}
measurements.push(measurement);
}
}
_populateLatency(measurements) {
const newIsPopulated = isPopulated(this._latencyMeasurement.new);
const oldIsPopulated = isPopulated(this._latencyMeasurement.old);
if (newIsPopulated || oldIsPopulated) {
const values = {};
if (newIsPopulated) {
values.new = this._latencyMeasurement.new;
}
if (oldIsPopulated) {
values.old = this._latencyMeasurement.old;
}
measurements.push({
key: 'latency_ms',
values,
});
}
}
}
exports.default = MigrationOpTracker;
//# sourceMappingURL=MigrationOpTracker.js.map