UNPKG

@launchdarkly/js-server-sdk-common

Version:
208 lines 8.26 kB
"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