@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
JavaScript
"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