UNPKG

@launchdarkly/js-server-sdk-common

Version:
188 lines 8.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const js_sdk_common_1 = require("@launchdarkly/js-sdk-common"); const serialization_1 = require("../store/serialization"); function selectorAsQueryParams(selector) { if (!selector) { return []; } return [ { key: 'basis', value: selector, }, ]; } // helper function to transform FDv1 response data into events the PayloadProcessor can parse function processFDv1FlagsAndSegments(payloadProcessor, data) { payloadProcessor.processEvents([ { event: `server-intent`, data: { payloads: [ { id: `FDv1Fallback`, target: 1, intentCode: `xfer-full`, }, ], }, }, ]); Object.entries((data === null || data === void 0 ? void 0 : data.flags) || []).forEach(([key, flag]) => { payloadProcessor.processEvents([ { event: `put-object`, data: { kind: 'flag', key, version: flag.version, object: flag, }, }, ]); }); Object.entries((data === null || data === void 0 ? void 0 : data.segments) || []).forEach(([key, segment]) => { payloadProcessor.processEvents([ { event: `put-object`, data: { kind: 'segment', key, version: segment.version, object: segment, }, }, ]); }); payloadProcessor.processEvents([ { event: `payload-transferred`, data: { state: `FDv1Fallback`, version: 1, }, }, ]); } /** * @internal */ class PollingProcessorFDv2 { /** * @param _requestor to fetch flags * @param _pollInterval in seconds controlling how frequently polling request is made * @param _logger for logging * @param _processResponseAsFDv1 defaults to false, but if set to true, this data source will process * the response body as FDv1 and convert it into a FDv2 payload. */ constructor(_requestor, _pollInterval = 30, _logger, _processResponseAsFDv1 = false) { this._requestor = _requestor; this._pollInterval = _pollInterval; this._logger = _logger; this._processResponseAsFDv1 = _processResponseAsFDv1; this._stopped = false; } _poll(dataCallback, statusCallback, selectorGetter) { var _a; if (this._stopped) { return; } const startTime = Date.now(); (_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Polling LaunchDarkly for feature flag updates'); this._requestor.requestAllData((err, body, headers) => { var _a, _b, _c, _d, _e, _f, _g; if (this._stopped) { return; } const elapsed = Date.now() - startTime; const sleepFor = Math.max(this._pollInterval * 1000 - elapsed, 0); (_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Elapsed: %d ms, sleeping for %d ms', elapsed, sleepFor); if (err) { const { status } = err; // this is a short term error and will be removed once FDv2 adoption is sufficient. if (err instanceof js_sdk_common_1.LDFlagDeliveryFallbackError) { (_b = this._logger) === null || _b === void 0 ? void 0 : _b.error(err.message); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, err); // It is not recoverable, return and do not trigger another poll. return; } if (status && !(0, js_sdk_common_1.isHttpRecoverable)(status)) { const message = (0, js_sdk_common_1.httpErrorMessage)(err, 'polling request'); (_c = this._logger) === null || _c === void 0 ? void 0 : _c.error(message); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, new js_sdk_common_1.LDPollingError(js_sdk_common_1.DataSourceErrorKind.ErrorResponse, message, status, false)); // It is not recoverable, return and do not trigger another poll. return; } const message = (0, js_sdk_common_1.httpErrorMessage)(err, 'polling request', 'will retry'); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Interrupted, new js_sdk_common_1.LDPollingError(js_sdk_common_1.DataSourceErrorKind.ErrorResponse, message, status)); (_d = this._logger) === null || _d === void 0 ? void 0 : _d.warn(message); // schedule poll this._timeoutHandle = setTimeout(() => { this._poll(dataCallback, statusCallback, selectorGetter); }, sleepFor); return; } const initMetadata = js_sdk_common_1.internal.initMetadataFromHeaders(headers); if (body) { try { const payloadProcessor = new js_sdk_common_1.internal.PayloadProcessor({ flag: (flag) => { (0, serialization_1.processFlag)(flag); return flag; }, segment: (segment) => { (0, serialization_1.processSegment)(segment); return segment; }, }, (errorKind, message) => { statusCallback(js_sdk_common_1.subsystem.DataSourceState.Interrupted, new js_sdk_common_1.LDPollingError(errorKind, message)); }, this._logger); payloadProcessor.addPayloadListener((payload) => { dataCallback(payload.basis, { initMetadata, payload }); }); (_e = this._logger) === null || _e === void 0 ? void 0 : _e.debug(`Got body: ${body}`); if (!this._processResponseAsFDv1) { // FDv2 case const parsed = JSON.parse(body); payloadProcessor.processEvents(parsed.events); } else { // FDv1 case const parsed = JSON.parse(body); processFDv1FlagsAndSegments(payloadProcessor, parsed); } statusCallback(js_sdk_common_1.subsystem.DataSourceState.Valid); } catch (_h) { // We could not parse this JSON. Report the problem and fallthrough to // start another poll. (_f = this._logger) === null || _f === void 0 ? void 0 : _f.error('Response contained invalid data'); (_g = this._logger) === null || _g === void 0 ? void 0 : _g.debug(`${err} - Body follows: ${body}`); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Interrupted, new js_sdk_common_1.LDPollingError(js_sdk_common_1.DataSourceErrorKind.InvalidData, 'Malformed data in polling response')); } } // schedule poll this._timeoutHandle = setTimeout(() => { this._poll(dataCallback, statusCallback, selectorGetter); }, sleepFor); }, selectorAsQueryParams(selectorGetter === null || selectorGetter === void 0 ? void 0 : selectorGetter())); } start(dataCallback, statusCallback, selectorGetter) { this._statusCallback = statusCallback; // hold reference for usage in stop() statusCallback(js_sdk_common_1.subsystem.DataSourceState.Initializing); this._poll(dataCallback, statusCallback, selectorGetter); } stop() { var _a; if (this._timeoutHandle) { clearTimeout(this._timeoutHandle); this._timeoutHandle = undefined; } (_a = this._statusCallback) === null || _a === void 0 ? void 0 : _a.call(this, js_sdk_common_1.subsystem.DataSourceState.Closed); this._stopped = true; this._statusCallback = undefined; } } exports.default = PollingProcessorFDv2; //# sourceMappingURL=PollingProcessorFDv2.js.map