@launchdarkly/js-server-sdk-common
Version:
LaunchDarkly Server SDK for JavaScript - common code
123 lines • 7.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const js_sdk_common_1 = require("@launchdarkly/js-sdk-common");
const serialization_1 = require("../store/serialization");
/**
* @internal
*/
class OneShotInitializerFDv2 {
constructor(_requestor, _logger) {
this._requestor = _requestor;
this._logger = _logger;
this._stopped = false;
}
start(dataCallback, statusCallback) {
var _a;
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Initializing);
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Performing initialization request to LaunchDarkly for feature flag data.');
this._requestor.requestAllData((err, body, headers, fallbackToFDv1) => {
var _a, _b, _c;
if (this._stopped) {
return;
}
// Helper used to emit the FDv1 fallback signal once any accompanying payload has been
// applied. Callers should `return` immediately after invoking it.
const emitFallback = () => {
var _a;
const status = err === null || err === void 0 ? void 0 : err.status;
const message = err
? (0, js_sdk_common_1.httpErrorMessage)(err, 'initializer', 'falling back to FDv1')
: `Response header indicates to fallback to FDv1`;
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.warn(message);
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, new js_sdk_common_1.LDFlagDeliveryFallbackError(js_sdk_common_1.DataSourceErrorKind.ErrorResponse, message, status));
};
if (err) {
// An error response can still carry the fallback directive. Emit the directive in
// that case so the CompositeDataSource can hand off to the FDv1 synchronizer.
if (fallbackToFDv1) {
emitFallback();
return;
}
const { status } = err;
const message = (0, js_sdk_common_1.httpErrorMessage)(err, 'initializer', 'initializer does not retry');
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error(message);
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, new js_sdk_common_1.LDPollingError(js_sdk_common_1.DataSourceErrorKind.ErrorResponse, message, status));
return;
}
if (!body) {
if (fallbackToFDv1) {
emitFallback();
return;
}
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, new js_sdk_common_1.LDPollingError(js_sdk_common_1.DataSourceErrorKind.InvalidData, 'One shot initializer response missing body.'));
return;
}
const initMetadata = js_sdk_common_1.internal.initMetadataFromHeaders(headers);
try {
const parsed = JSON.parse(body);
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) => {
// If a per-event error fires while the fallback directive is in flight, route it
// through the LDFlagDeliveryFallbackError path so CompositeDataSource still
// engages FDv1. Otherwise the directive would be lost: the composite's status
// handler would treat the LDPollingError as an ordinary failure and fall through
// to the next FDv2 source.
if (fallbackToFDv1) {
emitFallback();
return;
}
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Interrupted, new js_sdk_common_1.LDPollingError(errorKind, message));
}, this._logger);
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Valid);
// When the fallback directive rides along on a 200 response with a valid payload,
// the directive must be applied atomically with the data callback. CompositeData-
// Source disables its callback handler as soon as basis-during-init arrives, so a
// separate status callback emitted afterwards would be silently dropped. Instead,
// attach a fallbackToFDv1 marker to the data object: CompositeDataSource will swap
// its synchronizer list to FDv1 before resolving the switchToSync transition.
payloadProcessor.addPayloadListener((payload) => {
var _a;
const data = {
initMetadata,
payload,
};
if (fallbackToFDv1) {
data.fallbackToFDv1 = true;
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.warn(`Response header indicates to fallback to FDv1`);
}
dataCallback(payload.type === 'full', data);
});
payloadProcessor.processEvents(parsed.events);
// The fallback directive (if any) was already attached to the data callback above,
// so CompositeDataSource has already taken over the transition. Just close out.
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed);
}
catch (parseError) {
// We could not parse this JSON. Report the problem.
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.error('Response contained invalid data');
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.debug(`${parseError} - Body follows: ${body}`);
if (fallbackToFDv1) {
// Even when the accompanying body could not be parsed, the directive must still
// be honored so we don't get stuck retrying FDv2 indefinitely. No payload was
// applied here so we can use the status callback path safely.
emitFallback();
return;
}
statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, new js_sdk_common_1.LDPollingError(js_sdk_common_1.DataSourceErrorKind.InvalidData, 'Malformed data in polling response'));
}
});
}
stop() {
this._stopped = true;
}
}
exports.default = OneShotInitializerFDv2;
//# sourceMappingURL=OneShotInitializerFDv2.js.map