UNPKG

@glue42/bbg-market-data

Version:

A high-level API that wraps existing Glue42 Bloomberg Bridge Market Data interop methods. The API is based on the jBloomberg open source wrapper.

212 lines 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SubscriptionRequestImpl = void 0; const tslib_1 = require("tslib"); const callback_registry_1 = tslib_1.__importDefault(require("callback-registry")); const request_status_1 = require("../request-status"); const types_1 = require("./types"); const helpers_1 = require("./helpers"); const message_handlers_1 = require("./message-handlers"); const event_types_1 = require("./event-types"); const subscription_1 = require("./types/subscription"); const message_types_1 = require("./message-types"); const utils_1 = require("./../utils"); const correlationId_1 = tslib_1.__importDefault(require("./../correlationId")); const DATA_EVENT = "data-event"; const ERROR_EVENT = "error-event"; const STATUS_EVENT = "status-event"; const FAIL_EVENT = "fail-event"; const BBG_EVENT = "bbg-event"; /** * Represents the lifecycle of a subscription request. */ class SubscriptionRequestImpl { constructor(sessionManager, config, subscriptions, subscriptionDataHandler) { this.sessionManager = sessionManager; this.config = config; this.subscriptions = subscriptions; this.subscriptionDataHandler = subscriptionDataHandler; this.registry = callback_registry_1.default(); this._status = request_status_1.RequestStatus.Created; this.subscriptionsByInternalIdMap = new Map(); this.sessionInstance = this.sessionManager.getSubscriptionReqSessionInstance(); } /** * Public api. */ get api() { // eslint-disable-next-line @typescript-eslint/no-this-alias const requestImpl = this; return { get id() { return requestImpl.requestId; }, get settings() { return { operation: requestImpl.config.operation, service: requestImpl.config.service, }; }, get status() { return requestImpl._status; }, open: requestImpl.open.bind(requestImpl), close: requestImpl.close.bind(requestImpl), onData: (callback) => { return requestImpl.registry.add(DATA_EVENT, callback); }, onError: (callback) => { return requestImpl.registry.add(ERROR_EVENT, callback); }, onEvent: (callback) => { return requestImpl.registry.add(BBG_EVENT, callback); }, onStatus: (callback) => { utils_1.callSafe(callback, requestImpl._status); return requestImpl.registry.add(STATUS_EVENT, callback); }, onFail: (callback) => { return requestImpl.registry.add(FAIL_EVENT, callback); }, }; } open(options) { if (helpers_1.isPending(this._status)) { throw new Error("Request can be opened/reopened if its status is Created, Failed, Closed or Completed. Either close the request or wait it to complete."); } const _a = options !== null && options !== void 0 ? options : {}, { session } = _a, otherSettings = tslib_1.__rest(_a, ["session"]); this.sessionInstance = this.sessionManager.getSubscriptionReqSessionInstance(session); this.reset(); // Providing the service and operation as a suffix for the id to make troubleshooting easier. this.requestId = new correlationId_1.default(this.config.service, this.config.operation).value; const handleOpenSuccess = () => { this.requestActivated(); }; const handleOpenFail = (error) => { this.requestFailed(error); }; const handleEvent = (event) => { this.registry.execute(BBG_EVENT, event); }; const messageProcessor = (msg) => tslib_1.__awaiter(this, void 0, void 0, function* () { yield this.processEventMessage(msg); }); this.requestOpened(); const terminalSubscriptions = Array.from(this.subscriptionsByInternalIdMap.values()).map(helpers_1.toTerminalSubscription); this.sessionInstance.openSubscriptionRequest(this.requestId, this.config, terminalSubscriptions, otherSettings !== null && otherSettings !== void 0 ? otherSettings : {}, handleEvent, messageProcessor, handleOpenSuccess, handleOpenFail); } close() { return tslib_1.__awaiter(this, void 0, void 0, function* () { yield this.sessionInstance.closeRequest(this.requestId, () => this.requestClosed()); }); } processEventMessage(eventMessage) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const error = [ message_handlers_1.sessionStartUpFailureHandler(eventMessage), message_handlers_1.sessionTerminatedHandler(eventMessage), message_handlers_1.serviceOpenedFailureHandler(eventMessage), ].find(({ match }) => match); if (error != null) { this.requestFailed(error.data); return; } this.processSubscriptionErrors(eventMessage); if (helpers_1.isEventOfType(eventMessage, event_types_1.EventTypes.SubscriptionData)) { this.processSubscriptionData(eventMessage); } }); } processSubscriptionData(eventMessage) { try { const subscriptionDataByInternalIdMap = this.subscriptionDataHandler(eventMessage); const response = []; subscriptionDataByInternalIdMap.forEach((data, internalId) => { const subscription = this.subscriptionsByInternalIdMap.get(internalId); if (subscription) { response.push(Object.assign(Object.assign({}, data), { subscriptionId: subscription.subscriptionId, security: subscription.security })); } }); this.registry.execute(DATA_EVENT, response); } catch (error) { console.error(error); } } processSubscriptionErrors(eventMessage) { const subscriptionStatusHandler = (_a, messageType) => { var { correlationIds } = _a, eventMessage = tslib_1.__rest(_a, ["correlationIds"]); const errorsMap = new Map(); if (!helpers_1.isMessageOfType(eventMessage, messageType)) { return errorsMap; } const messageDetails = helpers_1.getMessageDetails(eventMessage, messageType); (correlationIds !== null && correlationIds !== void 0 ? correlationIds : []).forEach((id) => { errorsMap.set(id, messageDetails === null || messageDetails === void 0 ? void 0 : messageDetails.reason); }); return errorsMap; }; const errors = []; const failureErrorsByInternalIdMap = subscriptionStatusHandler(eventMessage, message_types_1.MessageTypes.SubscriptionFailure); failureErrorsByInternalIdMap.forEach((error, internalId) => { const subscription = this.subscriptionsByInternalIdMap.get(internalId); if (subscription) { subscription.status = subscription_1.SubscriptionStatus.SubscriptionFailure; error.subscriptionId = subscription.subscriptionId; } errors.push(error); }); const terminatedErrorsMap = subscriptionStatusHandler(eventMessage, message_types_1.MessageTypes.SubscriptionTerminated); terminatedErrorsMap.forEach((error, internalId) => { const subscription = this.subscriptionsByInternalIdMap.get(internalId); if (subscription) { subscription.status = subscription_1.SubscriptionStatus.SubscriptionTerminated; error.subscriptionId = subscription.subscriptionId; } errors.push(error); }); // Raise fail event only if errors are found. if (errors.length > 0) { this.raiseSubscriptionErrors(errors); const hasActiveSubscriptions = Array.from(this.subscriptionsByInternalIdMap.values()).some(({ status }) => status != subscription_1.SubscriptionStatus.SubscriptionFailure && status != subscription_1.SubscriptionStatus.SubscriptionTerminated); if (!hasActiveSubscriptions) { const error = new types_1.BloombergError(event_types_1.EventTypes.SubscriptionStatus, "SubscriptionFailure|SubscriptionTerminated", "All subscriptions are inactive. No more updates will be received for them."); return this.requestFailed(error); } } } raiseSubscriptionErrors(errors) { this.registry.execute(FAIL_EVENT, errors); } requestOpened() { this._status = request_status_1.RequestStatus.Opened; this.raiseStatusChanged(); } requestActivated() { this._status = request_status_1.RequestStatus.Active; this.raiseStatusChanged(); } requestClosed() { this._status = request_status_1.RequestStatus.Closed; this.raiseStatusChanged(); } requestFailed(error) { const reason = helpers_1.getFailureReason(this.requestId, error); this.sessionInstance.disposeRequest(this.requestId, reason); this._status = request_status_1.RequestStatus.Failed; this.raiseStatusChanged(); this.registry.execute(ERROR_EVENT, error); } raiseStatusChanged() { this.registry.execute(STATUS_EVENT, this._status); } reset() { this.subscriptionsByInternalIdMap.clear(); this.subscriptions.forEach((s) => { s = helpers_1.setSubscriptionIds(helpers_1.setSubscriptionCreatedStatus(s)); this.subscriptionsByInternalIdMap.set(s.internalId, s); }); } } exports.SubscriptionRequestImpl = SubscriptionRequestImpl; //# sourceMappingURL=subscription-request.js.map