barracuda-client-api
Version:
API Client to connect to Barracuda Enterprise Service Bus
1,084 lines • 73.6 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BarracudaPublisherCommandType = exports.BarracudaClient = exports.DefaultQueryTopValue = exports.BarracudaBridgeAggregateCommands = exports.BarracudaBridgeReadQueryCommands = exports.BarracudaCommand = void 0;
const IBarracudaClient_1 = require("./IBarracudaClient");
const index_1 = require("./logging/index");
const connectionProps_1 = require("./helpers/connectionProps");
const BarracudaBrowserWebSocket_1 = require("./BarracudaBrowserWebSocket");
const index_2 = require("./error/index");
const cross_fetch_1 = require("cross-fetch");
const BarracudaClientBuildDetail_1 = __importDefault(require("./BarracudaClientBuildDetail"));
// tslint:disable-next-line:no-require-imports
const ws_1 = __importDefault(require("ws"));
const uuid_1 = require("./react-native/uuid");
var BarracudaCommand;
(function (BarracudaCommand) {
BarracudaCommand["close"] = "close";
})(BarracudaCommand = exports.BarracudaCommand || (exports.BarracudaCommand = {}));
var BarracudaBridgeReadQueryCommands;
(function (BarracudaBridgeReadQueryCommands) {
BarracudaBridgeReadQueryCommands["aggregate"] = "aggregate";
BarracudaBridgeReadQueryCommands["diffSubscribe"] = "diffSubscribe";
BarracudaBridgeReadQueryCommands["snapshot"] = "snapshot";
BarracudaBridgeReadQueryCommands["snapshotAndDiffSubscribe"] = "snapshotAndDiffSubscribe";
BarracudaBridgeReadQueryCommands["snapshotAndSubscribe"] = "snapshotAndSubscribe";
BarracudaBridgeReadQueryCommands["subscribe"] = "subscribe";
})(BarracudaBridgeReadQueryCommands = exports.BarracudaBridgeReadQueryCommands || (exports.BarracudaBridgeReadQueryCommands = {}));
var BarracudaBridgeQueryCommands;
(function (BarracudaBridgeQueryCommands) {
BarracudaBridgeQueryCommands["unsubscribe"] = "unsubscribe";
})(BarracudaBridgeQueryCommands || (BarracudaBridgeQueryCommands = {}));
var BarracudaBridgeAggregateCommands;
(function (BarracudaBridgeAggregateCommands) {
BarracudaBridgeAggregateCommands["unsubscribe"] = "unsubscribe";
})(BarracudaBridgeAggregateCommands = exports.BarracudaBridgeAggregateCommands || (exports.BarracudaBridgeAggregateCommands = {}));
function createWebSocket(ep) {
if (typeof window === "undefined" || typeof ws_1.default === "undefined") {
return new ws_1.default(ep);
}
else {
return new BarracudaBrowserWebSocket_1.BarracudaBrowserWebSocket(ep);
}
}
exports.DefaultQueryTopValue = 50000;
function toBoolean(v) {
switch (typeof v) {
case "boolean":
return v;
case "string":
switch (v.toLocaleLowerCase()) {
case "true":
case "1":
case "on":
case "yes":
return true;
default:
return false;
}
default:
return !!v;
}
}
class BarracudaClient {
constructor(defaultProps) {
this.skipReconnecting = false;
this._requests = {};
this._staleNotice = {};
this._defaultRestPublishEndPoint = `https://barracuda-rest-dev.citadelgroup.com`;
this._connectedAckAtLeastOnce = false;
this._reconnectCounter = 0;
this._subscriptionHistory = [];
this._lastConnectionProps = this;
this._connectionCounter = 0;
this._reconnect = false;
this._loglevel = index_1.loglevels.info;
this.connectionState = IBarracudaClient_1.BarracudaConnectionStatus.unknown;
this._subscriptions = {};
this._uid = uuid_1.uuid4();
this.setProps(defaultProps);
}
static get Build() {
return BarracudaClientBuildDetail_1.default;
}
get appName() {
return this._appName;
}
get appVersion() {
return this._appVersion;
}
get lastPong() {
return this._lastPong;
}
get serverClientId() {
var _a;
return (_a = this.connectionAckMessage) === null || _a === void 0 ? void 0 : _a.clientId;
}
get serverDebug() {
return !!this._serverDebug;
}
get restPublishEndPoint() {
return this._defaultRestPublishEndPoint;
}
get gaToken() {
return this._gaToken;
}
get serviceHost() {
var _a;
return (_a = this.connectionAckMessage) === null || _a === void 0 ? void 0 : _a.host;
}
get reconnect() {
return this._reconnect;
}
set reconnect(value) {
this._reconnect = value;
}
get onConnectionError() {
return this._onConnectionError;
}
set onConnectionError(value) {
this._onConnectionError = value;
}
get onError() {
return this._onError;
}
set onError(value) {
this._onError = value;
}
get onConnectionStateChange() {
return this._onConnectionStateChange;
}
set onConnectionStateChange(value) {
this._onConnectionStateChange = value;
}
get onSubscriptionUpdate() {
return this._onSubscriptionUpdate;
}
set onSubscriptionUpdate(value) {
this._onSubscriptionUpdate = value;
}
get loglevel() {
return this._loglevel;
}
set loglevel(value) {
this._loglevel = value;
}
get uid() {
return this._uid;
}
get logUid() {
var _a;
return ((_a = this._logUid) !== null && _a !== void 0 ? _a : (this._logUid = this.uid.substr(this.uid.length - 5)));
}
get connectionAckMessage() {
return this._connectionAckMessage;
}
get lastConnectionProps() {
return this._lastConnectionProps;
}
get defaultConnectionProps() {
return { ...BarracudaClient.getConnectionProps(this) };
}
async connect(overrideProps) {
const connectionProps = BarracudaClient.getConnectionProps(this, overrideProps);
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(this.lastConnectionProps)}`, {
defaultConnectionProps: this.defaultConnectionProps,
overrideProps,
connectionProps,
});
}
if (!connectionProps.endpoint) {
throw new index_2.BarracudaError(`${this.bcLog(connectionProps)} Can't connect to an empty endpoint.`, {
connectionProps,
});
}
switch (this.connectionState) {
case IBarracudaClient_1.BarracudaConnectionStatus.connected:
case IBarracudaClient_1.BarracudaConnectionStatus.connectedAck:
if (index_1.isWarn(this.ll)) {
index_1.logWarn("Already connected. Cannot reconnect again without closing the connection first.", {
activeSubscription: Object.values(this._subscriptions).length,
activeRequests: Object.values(this._requests).filter((r) => !r.responseProcessed).length,
});
}
return Promise.resolve(this);
case IBarracudaClient_1.BarracudaConnectionStatus.connecting:
index_1.logWarn("Actively connecting. Cannot connect again without closing the connection first.", {
activeSubscription: Object.values(this._subscriptions).length,
activeRequests: Object.values(this._requests).filter((r) => !r.responseProcessed).length,
});
return Promise.resolve(this);
case IBarracudaClient_1.BarracudaConnectionStatus.reconnecting:
case IBarracudaClient_1.BarracudaConnectionStatus.disconnected:
case IBarracudaClient_1.BarracudaConnectionStatus.connectionError:
case IBarracudaClient_1.BarracudaConnectionStatus.unknown:
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.connecting, connectionProps);
break;
case IBarracudaClient_1.BarracudaConnectionStatus.closing:
index_1.logWarn("Closing, wait until closed.", {
activeSubscription: !this._subscriptions
? 0
: Object.values(this._subscriptions).length,
activeRequests: !this._requests
? 0
: Object.values(this._requests).filter((r) => !(r === null || r === void 0 ? void 0 : r.responseProcessed))
.length,
});
return Promise.resolve(this);
}
return new Promise(async (connectionPendingResolve, rejectPendingConnection) => {
try {
this._lastConnectionProps = connectionProps;
this.clearConnectionState();
const endPointUrlString = connectionProps_1.makeConnectionQuery(connectionProps, this.serverDebug);
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(connectionProps)} connecting Barracuda Client`, {
connectionProps,
endPointUrl: endPointUrlString,
});
}
else {
if (index_1.isInfo(this.ll)) {
index_1.logInfo(`${this.bcLog(connectionProps)} connecting Barracuda Client`);
}
}
this.ws = createWebSocket(endPointUrlString);
this.ws.once("message", (ack) => {
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(connectionProps)} WS first MSG ACK`, {
ack,
connectionProps,
});
}
try {
const ackMsg = JSON.parse(ack);
this._connectionAckMessage = ackMsg;
if (ackMsg.status === "Connected") {
this._connectedAckAtLeastOnce = true;
this._connectionCounter++;
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.connectedAck, connectionProps);
this.startSubscriptionListener(connectionProps);
try {
connectionPendingResolve(this);
}
catch (e) {
if (index_1.isWarn(this.ll)) {
index_1.logWarn(`${this.bcLog(connectionProps)} Unhandled exception in BCjs consumer. BCjs consumer should implement .catch and ensure it doesn't throw exceptions`, e);
}
}
}
else {
if (index_1.shouldLogErrors(this.ll)) {
index_1.logError(`${this.bcLog(connectionProps)} ackMessage contained status other than 'Connected' ${ackMsg.status}`, ackMsg);
}
}
}
catch (error) {
const err = new index_2.BarracudaError("Error while parsing ACK message", {
connectionProps,
originalError: error,
});
if (connectionProps === null || connectionProps === void 0 ? void 0 : connectionProps.onConnectionError) {
this.callHandlerSafely(connectionProps, () => { var _a; return (_a = connectionProps === null || connectionProps === void 0 ? void 0 : connectionProps.onConnectionError) === null || _a === void 0 ? void 0 : _a.call(this, err, this); }, { error: err }, "onConnectionError");
}
}
});
this.ws.once("open", () => {
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(connectionProps)} WS connected`, connectionProps);
}
this._reconnectCounter = 0;
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.connected, connectionProps);
});
this.ws.once("close", (code, reason) => {
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.disconnected, connectionProps);
const closeData = { code, reason };
if (index_1.isInfo(this.ll)) {
index_1.logInfo(`${this.bcLog(connectionProps)} closed ==>`, closeData);
}
if (this._connectedAckAtLeastOnce &&
toBoolean(connectionProps === null || connectionProps === void 0 ? void 0 : connectionProps.reconnect)) {
const delay = 1000 * ++this._reconnectCounter + Math.random() * 1000;
this.scheduleReconnect(connectionProps, delay);
}
else {
if (rejectPendingConnection) {
try {
rejectPendingConnection(new index_2.BarracudaError("[closed]", {
...closeData,
connectionProps,
msg: this.connectionAckMessage,
connectedAtLeastOnce: this._connectedAckAtLeastOnce,
}));
}
catch (e) {
// prevent bubbling up unhandled exceptions in reject clause. Consumer should implement .catch on promise
if (index_1.isWarn(this.ll)) {
index_1.logWarn(`Unhandled exception in BCjs consumer. BCjs consumer should implement .catch and ensure it doesn't throw/bubble exceptions`, e);
}
}
}
}
});
this.ws.on("error", (error) => {
if (this.connectionState === IBarracudaClient_1.BarracudaConnectionStatus.closing) {
if (index_1.isWarn(this.ll)) {
index_1.logWarn(`${this.bcLog(connectionProps)} ignoring processing errors while closing ==>`, { error });
}
return;
}
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.connectionError, connectionProps);
if (index_1.shouldLogErrors(this.ll)) {
index_1.logError(`${this.bcLog(connectionProps)} error ==>`, {
connectionState: this.connectionState,
error,
});
}
if (connectionProps === null || connectionProps === void 0 ? void 0 : connectionProps.onConnectionError) {
this.callHandlerSafely(connectionProps, () => { var _a; return (_a = connectionProps === null || connectionProps === void 0 ? void 0 : connectionProps.onConnectionError) === null || _a === void 0 ? void 0 : _a.call(this, error, this); }, {
error,
msg: this.connectionAckMessage,
}, "onConnectionError");
}
});
}
catch (error) {
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.connectionError, connectionProps);
try {
if (rejectPendingConnection) {
rejectPendingConnection(error);
}
}
catch (e) {
if (index_1.isWarn(this.ll)) {
index_1.logWarn(`Unhandled exception in BCjs consumer. BCjs consumer should implement .catch and ensure it doesn't throw exceptions`, e);
}
}
}
});
}
_emulateServerDisconnect() {
var _a;
const message = `${this.bcLog(this.lastConnectionProps)} Emulating server disconnect`;
const serverCloseMessage = {
messageType: "IBarracudaBridgeRequest",
requestId: uuid_1.uuid4(),
command: BarracudaCommand.close,
messsage: message,
};
if (index_1.isInfo(this.ll)) {
index_1.logInfo(message, serverCloseMessage);
}
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(serverCloseMessage));
}
close() {
var _a;
const props = this.lastConnectionProps;
const reason = `[${this.logUid}] [sUid:${this.serverClientId}] connection gracefully closed by API consumer`;
const code = 1000;
switch (this.connectionState) {
case IBarracudaClient_1.BarracudaConnectionStatus.connected:
case IBarracudaClient_1.BarracudaConnectionStatus.connectedAck:
case IBarracudaClient_1.BarracudaConnectionStatus.connecting:
case IBarracudaClient_1.BarracudaConnectionStatus.connectionError:
case IBarracudaClient_1.BarracudaConnectionStatus.reconnecting:
if (index_1.isInfo(this.ll)) {
index_1.logInfo(`${this.bcLog(props)} closing connection`, {
connectionState: this.connectionState,
code,
reason,
});
}
break;
case IBarracudaClient_1.BarracudaConnectionStatus.unknown:
case IBarracudaClient_1.BarracudaConnectionStatus.disconnected:
case IBarracudaClient_1.BarracudaConnectionStatus.closing:
if (index_1.isWarn(this.ll)) {
index_1.logWarn(`${this.bcLog(props)} Can not close this client, it is already [${this.connectionState}]`);
}
return;
default:
break;
}
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.closing, props);
this.skipReconnecting = true;
if (this._staleTriggerTimer) {
clearTimeout(this._staleTriggerTimer);
this._staleTimeOutMs = 0;
}
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.close(code, reason);
}
get connectionCounter() {
return this._connectionCounter;
}
bcLog(props) {
if (this.serverClientId) {
return `[${props.endpoint}] [${this.logUid}] [sUid:${this.serverClientId}]`;
}
else {
return `[${props.endpoint}] [${this.logUid}]`;
}
}
disconnect() {
return new Promise((resolve, reject) => {
var _a;
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.once("close", (code, reason) => {
const closeConfirmation = { code, reason };
resolve(closeConfirmation);
});
this.close();
});
}
get subscriptions() {
return Object.values(this._subscriptions).map((s) => BarracudaClient.shareSubscriptionDetails(s));
}
get previousSessionSubscriptions() {
if (this._subscriptionHistory.length === 0) {
return [];
}
const lastSubscriptions = this._subscriptionHistory[this._subscriptionHistory.length - 1];
return Object.values(lastSubscriptions).map((s) => BarracudaClient.shareSubscriptionDetails(s));
}
snapshot(instance, topic, message, filter, top = exports.DefaultQueryTopValue, project, sort, heartbeatPeriod = 0) {
return this.subscribeQuery(BarracudaBridgeReadQueryCommands.snapshot, {
instance,
topic,
filter,
top,
onMessage: (batch, subscriptionId) => {
if (message) {
message({
subscriptionId,
messages: batch,
});
}
},
project,
sort,
heartbeatPeriod,
}).then((sub) => {
return sub.subscriptionId;
});
}
snapshotAndSubscribe(instance, topic, message, filter, top = exports.DefaultQueryTopValue, project, sort, heartbeatPeriod = 0) {
return this.subscribeQuery(BarracudaBridgeReadQueryCommands.snapshotAndSubscribe, {
instance,
topic,
filter,
top,
onMessage: (batch, subscriptionId) => {
if (message) {
message({
subscriptionId,
messages: batch,
});
}
},
project,
sort,
heartbeatPeriod,
}).then((sub) => {
return sub.subscriptionId;
});
}
snapshotAndDiffSubscribe(instance, topic, message, filter, top = exports.DefaultQueryTopValue, project, sort, heartbeatPeriod = 0) {
return this.subscribeQuery(BarracudaBridgeReadQueryCommands.snapshotAndDiffSubscribe, {
instance,
topic,
filter,
top,
onMessage: (batch, subscriptionId) => {
if (message) {
message({
subscriptionId,
messages: batch,
});
}
},
project,
sort,
heartbeatPeriod,
}).then((sub) => {
return sub.subscriptionId;
});
}
subscribeDiff(instance, topic, message, filter, top = exports.DefaultQueryTopValue, project, sort, heartbeatPeriod = 0) {
return this.subscribeQuery(BarracudaBridgeReadQueryCommands.diffSubscribe, {
instance,
topic,
filter,
top,
onMessage: (batch, subscriptionId) => {
if (message) {
message({
subscriptionId,
messages: batch,
});
}
},
project,
sort,
heartbeatPeriod,
}).then((sub) => {
return sub.subscriptionId;
});
}
subscribe(instance, topic, message, filter, top = exports.DefaultQueryTopValue, project, sort, heartbeatPeriod = 0) {
return this.subscribeQuery(BarracudaBridgeReadQueryCommands.subscribe, {
instance,
topic,
filter,
top,
onMessage: (batch, subscriptionId) => {
if (message) {
message({
subscriptionId,
messages: batch,
});
}
},
project,
sort,
heartbeatPeriod,
}).then((sub) => {
return sub.subscriptionId;
});
}
async unsubscribe(subscriptionId) {
const props = this.lastConnectionProps;
const subDetails = this._subscriptions[subscriptionId];
if (!subDetails) {
throw new Error(`Unable to find internal details for [subId: ${subscriptionId}]. Please confirm subscriptionId veracity`);
}
if ((subDetails === null || subDetails === void 0 ? void 0 : subDetails.unsubscribed) ||
(subDetails === null || subDetails === void 0 ? void 0 : subDetails.lastServerStatus) ===
IBarracudaClient_1.BarracudaBridgeSubscriptionStatus.unsubscribed) {
index_1.logWarn(`Subscription ${subscriptionId} is already unsubscribed`, BarracudaClient.shareSubscriptionDetails(subDetails));
return;
}
if (index_1.isInfo(this.ll)) {
index_1.logInfo(`Unsubscribing ${subscriptionId}`);
}
subDetails.onMessage = undefined;
this._subscriptions[subscriptionId].pendingUnsubscribe = true;
const unsubscribeMsg = {
requestId: uuid_1.uuid4(),
messageType: "IBarracudaBridgeSubscriptionRequest",
command: BarracudaBridgeQueryCommands.unsubscribe,
heartbeatPeriod: 0,
subscriptionId,
};
const response = await this.sendRequest(unsubscribeMsg);
if (props === null || props === void 0 ? void 0 : props.onSubscriptionUpdate) {
subDetails.lastServerStatus =
IBarracudaClient_1.BarracudaBridgeSubscriptionStatus.unsubscribeRequestAcknowledged;
subDetails.lastServerStatusDt = new Date();
this.safeTriggerOnSubscriptionChanged(BarracudaClient.shareSubscriptionDetails(subDetails), props);
}
if (response.success) {
subDetails.unsubscribed = true;
}
}
async aggregate(topic, pipeline, instance, maxTimeMS = 5 * 1000, batchSize = 100) {
return new Promise(async (resolve, reject) => {
let results = [];
let subId;
const onSubscriptionResponse = (subResponse) => {
subResponse.messages.forEach((m) => {
switch (m.command) {
case IBarracudaClient_1.BarracudaBridgeResponseCommandTypes.sow:
results.push(m.data);
break;
case IBarracudaClient_1.BarracudaBridgeResponseCommandTypes.publish:
results.push(m.data);
break;
case IBarracudaClient_1.BarracudaBridgeResponseCommandTypes.groupEnd:
resolve(results);
break;
}
});
};
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(this.lastConnectionProps)} aggregate`, {
topic,
instance,
pipeline,
maxTimeMS,
batchSize,
});
}
const query = {
instance,
topic,
batchSize,
pipeline,
batchPeriod: maxTimeMS,
onMessage: onSubscriptionResponse,
};
try {
await this.requestQueryCommand(BarracudaBridgeReadQueryCommands.aggregate, query, undefined);
}
catch (e) {
reject(e);
}
});
}
async subscribeQuery(commandType, queryDetails) {
var _a;
let subId;
const onSubscriptionResponse = (subResponse) => {
var _a;
(_a = queryDetails.onMessage) === null || _a === void 0 ? void 0 : _a.call(this, subResponse.messages, subId);
};
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(this.lastConnectionProps)} subscribeQuery`, queryDetails);
}
const query = {
...queryDetails,
project: queryDetails.project,
orderBy: queryDetails.sort,
top: (_a = queryDetails === null || queryDetails === void 0 ? void 0 : queryDetails.top) !== null && _a !== void 0 ? _a : exports.DefaultQueryTopValue,
onMessage: onSubscriptionResponse,
};
subId = await this.requestQueryCommand(commandType, query, queryDetails);
return {
subscriptionId: subId,
unsubscribe: () => this.unsubscribe(subId),
};
}
getTopicInfo(topic, infoType, instance) {
if (index_1.isVerbose(this.ll)) {
index_1.logVerbose("getTopicInfo", { topic, infoType, instance }, this.instance);
}
const database = instance !== null && instance !== void 0 ? instance : this.instance;
if (!database) {
throw new Error("Instance is required. please provide either a default instance in the constructor, or pass an override instance in the parameter");
}
const requestBase = BarracudaClient.createRequest("IBarracudaBridgeCommandRequest");
let command;
let getResult;
switch (infoType) {
case IBarracudaClient_1.BarracudaTopicInfoType.barracuda_keys:
command = {
listIndexes: topic,
};
getResult = (response) => {
var _a, _b;
const indices = (_a = response === null || response === void 0 ? void 0 : response.cursor) === null || _a === void 0 ? void 0 : _a.firstBatch;
return {
result: (_b = indices === null || indices === void 0 ? void 0 : indices.find((i) => "_" + IBarracudaClient_1.BarracudaTopicInfoType.barracuda_keys === (i === null || i === void 0 ? void 0 : i.name))) === null || _b === void 0 ? void 0 : _b.key,
};
};
break;
case IBarracudaClient_1.BarracudaTopicInfoType.documentsCount:
command = {
count: topic,
};
getResult = (response) => ({ result: response === null || response === void 0 ? void 0 : response.n });
break;
case IBarracudaClient_1.BarracudaTopicInfoType.size:
command = {
dataSize: `${instance}.${topic}`,
};
getResult = (response) => ({ result: response === null || response === void 0 ? void 0 : response.size });
break;
default:
throw new index_2.BarracudaError(`Unidentified infoType: ${infoType}`, {
topic,
instance,
});
}
const req = {
...requestBase,
database,
command: {
...command,
},
};
if (index_1.isInfo(this.ll)) {
index_1.logInfo(`Requesting TopicInfo ${topic}`, index_1.isDebug(this.ll) ? req : undefined);
}
return this.sendRequest(req).then((r) => {
if (this.throwIfMissingField(r, "response")) {
return;
}
return getResult(r.response);
});
}
setOnStaleSubscriptionAlert(onStale, seconds = 60) {
this._staleTimeOutMs = seconds * 1000;
this._onStale = onStale;
if (this._staleTimeOutMs && this._onStale) {
if (!this._staleTriggerTimer) {
clearTimeout(this._staleTriggerTimer);
}
setTimeout(() => this.detectStaleSubscriptions(), this.staleTimeOutMs);
}
}
get endpoint() {
return this._endpoint;
}
set endpoint(value) {
this._endpoint = value;
}
get instance() {
return this._defaultInstance;
}
set instance(value) {
this._defaultInstance = value;
}
get staleTimeOutMs() {
var _a;
return (_a = this._staleTimeOutMs) !== null && _a !== void 0 ? _a : 0;
}
get ll() {
return this._loglevel;
}
static getConnectionProps(defaultProps, overrideProps) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
return {
endpoint: (_a = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.endpoint) !== null && _a !== void 0 ? _a : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.endpoint,
onConnectionError: (_b = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.onConnectionError) !== null && _b !== void 0 ? _b : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.onConnectionError,
onConnectionStateChange: (_c = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.onConnectionStateChange) !== null && _c !== void 0 ? _c : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.onConnectionStateChange,
onError: (_d = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.onError) !== null && _d !== void 0 ? _d : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.onError,
reconnect: (_e = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.reconnect) !== null && _e !== void 0 ? _e : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.reconnect,
gaToken: (_f = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.gaToken) !== null && _f !== void 0 ? _f : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.gaToken,
instance: (_g = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.instance) !== null && _g !== void 0 ? _g : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.instance,
onSubscriptionUpdate: (_h = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.onSubscriptionUpdate) !== null && _h !== void 0 ? _h : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.onSubscriptionUpdate,
loglevel: (_j = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.loglevel) !== null && _j !== void 0 ? _j : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.loglevel,
publishRestEndPoint: (_k = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.publishRestEndPoint) !== null && _k !== void 0 ? _k : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.publishRestEndPoint,
serverDebug: (_l = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.serverDebug) !== null && _l !== void 0 ? _l : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.serverDebug,
appName: (_m = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.appName) !== null && _m !== void 0 ? _m : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.appName,
appVersion: (_o = overrideProps === null || overrideProps === void 0 ? void 0 : overrideProps.appVersion) !== null && _o !== void 0 ? _o : defaultProps === null || defaultProps === void 0 ? void 0 : defaultProps.appVersion,
};
}
static createRequest(messageType) {
return this.createBBMsgType(messageType, {
requestId: uuid_1.uuid4(),
});
}
static createBBMsgType(messageType, bMsg) {
return {
messageType,
...bMsg,
};
}
static createRequestSubscription(commandType, instance, topic, queryDetails) {
var _a;
let filter;
if (typeof queryDetails.filter === "object") {
filter = JSON.stringify(queryDetails.filter);
}
else {
filter = queryDetails.filter;
}
// let pipeline: string | undefined;
// if (Array.isArray(queryDetails.pipeline)) {
// pipeline = JSON.stringify(queryDetails.pipeline);
// } else {
// pipeline = queryDetails.pipeline;
// }
return {
...BarracudaClient.createRequest("IBarracudaBridgeSubscriptionRequest"),
command: commandType,
database: instance,
collection: topic,
filter,
pipeline: queryDetails.pipeline,
projection: queryDetails.project,
orderBy: queryDetails.orderBy,
batchSize: queryDetails.batchSize,
batchPeriod: queryDetails.batchPeriod,
heartbeatPeriod: (_a = queryDetails.heartbeatPeriod) !== null && _a !== void 0 ? _a : 0,
top: queryDetails.top,
};
}
static shareSubscriptionDetails(subDetails) {
const copy = { ...subDetails };
// @ts-ignore
delete copy.inboundQueue;
delete copy.onMessage;
return copy;
}
static hasError(msg) {
var _a;
return ((msg === null || msg === void 0 ? void 0 : msg.success) === false || (!!(msg === null || msg === void 0 ? void 0 : msg.error) && ((_a = msg === null || msg === void 0 ? void 0 : msg.error) === null || _a === void 0 ? void 0 : _a.trim()) !== ""));
}
async send(topic, msg, type, props) {
return this.sendMessage(topic, msg, type, props);
}
sendWithKey(topic, key, msg) {
throw new Error("Not implemented yet. Contact Francesco Laurita to enable sending messages with custom keys");
}
safeTriggerOnSubscriptionChanged(subscriptionStatus, props) {
if (props.onSubscriptionUpdate) {
this.callHandlerSafely(props, () => { var _a; return (_a = props.onSubscriptionUpdate) === null || _a === void 0 ? void 0 : _a.call(this, subscriptionStatus, this); }, { subscriptionStatus }, "onSubscriptionUpdate");
}
}
safeTriggerOnStale(subscriptionStatus) {
this.callHandlerSafely(this.lastConnectionProps, () => { var _a; return (_a = this._onStale) === null || _a === void 0 ? void 0 : _a.call(this, subscriptionStatus, this); }, { subscriptionStatus }, "onStale");
this._staleNotice[subscriptionStatus.subscriptionId] =
subscriptionStatus.lastServerStatusDt;
}
async sendMessage(topic, msg, type, props) {
var _a, _b, _c;
let bMsg;
const sendProps = {
onError: (_a = props === null || props === void 0 ? void 0 : props.onError) !== null && _a !== void 0 ? _a : this.onError,
publishRestEndPoint: (_b = props === null || props === void 0 ? void 0 : props.publishRestEndPoint) !== null && _b !== void 0 ? _b : this.restPublishEndPoint,
gaToken: (_c = props === null || props === void 0 ? void 0 : props.gaToken) !== null && _c !== void 0 ? _c : this === null || this === void 0 ? void 0 : this.gaToken,
};
if (!topic) {
const err = "Can not send a message without a topic";
const errObj = new index_2.BarracudaError(err, { msg });
this.handleSendError(errObj, sendProps);
return undefined;
}
if (!msg) {
const err = "Can not send an empty message";
const errObj = new index_2.BarracudaError(err, { msg });
this.handleSendError(errObj, sendProps);
return undefined;
}
if (typeof msg === "string") {
try {
bMsg = JSON.parse(msg);
}
catch (e) {
const errObj = new index_2.BarracudaError("Can not send an un-parsable JSON message. JSON messages needs to be well formed.", {
originalError: e,
msg,
});
this.handleSendError(errObj, sendProps);
return undefined;
}
}
else {
bMsg = msg;
}
if (bMsg["_barracuda_meta"]) {
bMsg["_barracuda_meta"] = {
...bMsg["_barracuda_meta"],
bPublisherTst: Date.now(),
};
}
else {
bMsg._barracuda_meta = {
_payload_ver: 1,
_ver: 4,
bPublisherTst: Date.now(),
bPublisherCommand: type,
expiration: 0,
sequenceId: 0,
topic,
bPublisherClient: {
lang: BarracudaClientBuildDetail_1.default.name,
version: BarracudaClientBuildDetail_1.default.version,
},
bPublisherName: this.uid,
};
}
const requestHeaders = new cross_fetch_1.Headers({
"Content-Type": "application/json",
});
requestHeaders.append("gaToken", sendProps.gaToken);
const init = {
body: JSON.stringify(bMsg, null, "").trim(),
method: "POST",
headers: requestHeaders,
};
const info = `${sendProps === null || sendProps === void 0 ? void 0 : sendProps.publishRestEndPoint}/${type}/${topic}`;
if (index_1.isInfo(this.ll)) {
index_1.logInfo(`Sending Request to ${info}`, init);
}
let r;
try {
r = await cross_fetch_1.fetch(info, init);
}
catch (error) {
const errObj = new index_2.BarracudaError(`Error while sending msg through REST to ${info}`, {
httpRequest: { info, init },
originalError: error,
});
this.handleSendError(errObj, sendProps);
return undefined;
}
const responseHeader = r.headers;
const xRay = JSON.stringify(responseHeader === null || responseHeader === void 0 ? void 0 : responseHeader.get("x-ray"));
let responseBody;
try {
responseBody = await r.json();
}
catch (error) {
let body = "";
try {
body = await r.text();
}
catch (e) {
index_1.logError(`Couldn't get text body from server response`, {
httpRequest: {
info,
init,
},
});
}
const errObj = new index_2.BarracudaError("Error while json parsing server response.", {
httpRequest: {
info,
init,
},
httpResponse: {
"x-ray": xRay,
body,
headers: responseHeader,
},
originalError: error,
});
this.handleSendError(errObj, sendProps);
throw errObj;
}
if (responseBody.reply && responseBody.reply.indexOf("enqueued to ") >= 0) {
const piranhaResponse = {
...responseBody,
"x-ray": xRay,
url: sendProps === null || sendProps === void 0 ? void 0 : sendProps.publishRestEndPoint,
};
if (index_1.isInfo(this.ll)) {
index_1.logInfo("REST Publish Success ==>", piranhaResponse);
}
if (index_1.isDebug(this.ll)) {
index_1.logDebug("REST Publish Success ==>", {
...piranhaResponse,
headers: responseHeader,
});
}
return piranhaResponse;
}
else {
const errObj = new index_2.BarracudaError(`Received failure response from server ==> ${responseBody}`, {
httpRequest: {
info,
init,
},
httpResponse: {
"x-ray": xRay,
body: responseBody,
header: responseHeader,
},
url: sendProps === null || sendProps === void 0 ? void 0 : sendProps.publishRestEndPoint,
});
this.handleSendError(errObj, sendProps);
throw errObj;
}
}
handleSendError(err, sendProps, logContext = { error: err }) {
if (index_1.shouldLogErrors(this.ll)) {
index_1.logError(err);
}
if (sendProps === null || sendProps === void 0 ? void 0 : sendProps.onError) {
this.callHandlerSafely(this.lastConnectionProps, () => { var _a; return (_a = sendProps === null || sendProps === void 0 ? void 0 : sendProps.onError) === null || _a === void 0 ? void 0 : _a.call(this, err, this); }, logContext, "onError");
}
else {
throw err;
}
}
setProps(props) {
if (props === null || props === void 0 ? void 0 : props.loglevel) {
this.loglevel = props.loglevel;
}
if (index_1.isDebug(this.ll)) {
index_1.logDebug("BarracudaClient setting props", props);
}
if (props === null || props === void 0 ? void 0 : props.endpoint) {
this._endpoint = props.endpoint;
}
if (props === null || props === void 0 ? void 0 : props.publishRestEndPoint) {
this._defaultRestPublishEndPoint = props.publishRestEndPoint;
}
if (props === null || props === void 0 ? void 0 : props.serverDebug) {
this._serverDebug = props.serverDebug;
}
if ((props === null || props === void 0 ? void 0 : props.reconnect) !== undefined) {
this.reconnect = props.reconnect;
}
if (props === null || props === void 0 ? void 0 : props.onError) {
this.onError = props.onError;
}
if (props === null || props === void 0 ? void 0 : props.onConnectionError) {
this.onConnectionError = props.onConnectionError;
}
if (props === null || props === void 0 ? void 0 : props.onConnectionStateChange) {
this.onConnectionStateChange = props.onConnectionStateChange;
}
if (props === null || props === void 0 ? void 0 : props.onSubscriptionUpdate) {
this.onSubscriptionUpdate = props.onSubscriptionUpdate;
}
if (props === null || props === void 0 ? void 0 : props.instance) {
this.instance = props === null || props === void 0 ? void 0 : props.instance;
}
if (props === null || props === void 0 ? void 0 : props.gaToken) {
this._gaToken = props === null || props === void 0 ? void 0 : props.gaToken;
}
if (props === null || props === void 0 ? void 0 : props.appName) {
this._appName = props.appName;
}
if (props === null || props === void 0 ? void 0 : props.appVersion) {
this._appVersion = props.appVersion;
}
}
triggerOnConnectionStateChange(conState, connectionProps) {
this.connectionState = conState;
const connectionStatusHandler = connectionProps === null || connectionProps === void 0 ? void 0 : connectionProps.onConnectionStateChange;
switch (conState) {
case IBarracudaClient_1.BarracudaConnectionStatus.connecting:
case IBarracudaClient_1.BarracudaConnectionStatus.connected:
case IBarracudaClient_1.BarracudaConnectionStatus.reconnecting:
case IBarracudaClient_1.BarracudaConnectionStatus.closing:
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(connectionProps)} connection state: ${conState}`);
}
break;
case IBarracudaClient_1.BarracudaConnectionStatus.disconnected:
case IBarracudaClient_1.BarracudaConnectionStatus.connectedAck:
if (index_1.isInfo(this.ll)) {
index_1.logInfo(`${this.bcLog(connectionProps)} connection state: ${conState}`);
}
break;
case IBarracudaClient_1.BarracudaConnectionStatus.connectionError:
if (index_1.shouldLogErrors(this.ll)) {
index_1.logError(`${this.bcLog(connectionProps)} connection state: ${conState}`);
}
break;
}
if (connectionStatusHandler) {
this.callHandlerSafely(connectionProps, () => connectionStatusHandler === null || connectionStatusHandler === void 0 ? void 0 : connectionStatusHandler.call(this, conState, this), { connectionState: conState }, "onConnectionStateChange");
}
}
scheduleReconnect(props, delay = 1000) {
if (!this.skipReconnecting) {
this.triggerOnConnectionStateChange(IBarracudaClient_1.BarracudaConnectionStatus.reconnecting, this.lastConnectionProps);
if (index_1.isInfo(this.ll)) {
const logArgs = index_1.isDebug(this.ll)
? {
lastConnectionProps: this.lastConnectionProps,
newConnectionProps: props,
delay,
}
: undefined;
index_1.logInfo(`Scheduling reconnection in ${delay === null || delay === void 0 ? void 0 : delay.toFixed(2)}ms`, logArgs);
}
setTimeout(() => this.connect(props), delay);
}
}
callHandlerSafely(logCProps, handler, logContext, handlerName) {
if (index_1.isDebug(this.ll)) {
index_1.logDebug(`${this.bcLog(logCProps)} calling ${handlerName}`, {
...logContext,
connectionProps: logCProps,
});
}
try {
handler();
}
catch (error) {
const err = new index_2.BarracudaError(`Error while executing handler [${handlerName}]`, {
originalError: error,
handlerName: handlerName,
handlerContext: logContext,
connectionProps: logCProps,
});
if (index_1.shouldLogErrors(this.ll)) {
index_1.logError(err, { connectionProps: logCProps });
}
}
}
startSubscriptionListener(props) {
var _a;
if (this.connectionState !== IBarracudaClient_1.BarracudaConnectionStatus.connectedAck) {
return;
}
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.on("message", (msg) => {
if (this.connectionState !== IB