pubnub
Version:
Publish & Subscribe Real-time Messaging with PubNub
744 lines (628 loc) • 26.9 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _cryptography = _interopRequireDefault(require("../components/cryptography"));
var _config = _interopRequireDefault(require("../components/config"));
var _listener_manager = _interopRequireDefault(require("../components/listener_manager"));
var _reconnection_manager = _interopRequireDefault(require("../components/reconnection_manager"));
var _deduping_manager = _interopRequireDefault(require("../components/deduping_manager"));
var _utils = _interopRequireDefault(require("../utils"));
var _flow_interfaces = require("../flow_interfaces");
var _categories = _interopRequireDefault(require("../constants/categories"));
var _default = function () {
function _default(_ref) {
var subscribeEndpoint = _ref.subscribeEndpoint,
leaveEndpoint = _ref.leaveEndpoint,
heartbeatEndpoint = _ref.heartbeatEndpoint,
setStateEndpoint = _ref.setStateEndpoint,
timeEndpoint = _ref.timeEndpoint,
getFileUrl = _ref.getFileUrl,
config = _ref.config,
crypto = _ref.crypto,
listenerManager = _ref.listenerManager;
(0, _classCallCheck2["default"])(this, _default);
(0, _defineProperty2["default"])(this, "_crypto", void 0);
(0, _defineProperty2["default"])(this, "_config", void 0);
(0, _defineProperty2["default"])(this, "_listenerManager", void 0);
(0, _defineProperty2["default"])(this, "_reconnectionManager", void 0);
(0, _defineProperty2["default"])(this, "_leaveEndpoint", void 0);
(0, _defineProperty2["default"])(this, "_heartbeatEndpoint", void 0);
(0, _defineProperty2["default"])(this, "_setStateEndpoint", void 0);
(0, _defineProperty2["default"])(this, "_subscribeEndpoint", void 0);
(0, _defineProperty2["default"])(this, "_getFileUrl", void 0);
(0, _defineProperty2["default"])(this, "_channels", void 0);
(0, _defineProperty2["default"])(this, "_presenceChannels", void 0);
(0, _defineProperty2["default"])(this, "_heartbeatChannels", void 0);
(0, _defineProperty2["default"])(this, "_heartbeatChannelGroups", void 0);
(0, _defineProperty2["default"])(this, "_channelGroups", void 0);
(0, _defineProperty2["default"])(this, "_presenceChannelGroups", void 0);
(0, _defineProperty2["default"])(this, "_currentTimetoken", void 0);
(0, _defineProperty2["default"])(this, "_lastTimetoken", void 0);
(0, _defineProperty2["default"])(this, "_storedTimetoken", void 0);
(0, _defineProperty2["default"])(this, "_region", void 0);
(0, _defineProperty2["default"])(this, "_subscribeCall", void 0);
(0, _defineProperty2["default"])(this, "_heartbeatTimer", void 0);
(0, _defineProperty2["default"])(this, "_subscriptionStatusAnnounced", void 0);
(0, _defineProperty2["default"])(this, "_autoNetworkDetection", void 0);
(0, _defineProperty2["default"])(this, "_isOnline", void 0);
(0, _defineProperty2["default"])(this, "_pendingChannelSubscriptions", void 0);
(0, _defineProperty2["default"])(this, "_pendingChannelGroupSubscriptions", void 0);
(0, _defineProperty2["default"])(this, "_dedupingManager", void 0);
this._listenerManager = listenerManager;
this._config = config;
this._leaveEndpoint = leaveEndpoint;
this._heartbeatEndpoint = heartbeatEndpoint;
this._setStateEndpoint = setStateEndpoint;
this._subscribeEndpoint = subscribeEndpoint;
this._getFileUrl = getFileUrl;
this._crypto = crypto;
this._channels = {};
this._presenceChannels = {};
this._heartbeatChannels = {};
this._heartbeatChannelGroups = {};
this._channelGroups = {};
this._presenceChannelGroups = {};
this._pendingChannelSubscriptions = [];
this._pendingChannelGroupSubscriptions = [];
this._currentTimetoken = 0;
this._lastTimetoken = 0;
this._storedTimetoken = null;
this._subscriptionStatusAnnounced = false;
this._isOnline = true;
this._reconnectionManager = new _reconnection_manager["default"]({
timeEndpoint: timeEndpoint
});
this._dedupingManager = new _deduping_manager["default"]({
config: config
});
}
(0, _createClass2["default"])(_default, [{
key: "adaptStateChange",
value: function adaptStateChange(args, callback) {
var _this = this;
var state = args.state,
_args$channels = args.channels,
channels = _args$channels === void 0 ? [] : _args$channels,
_args$channelGroups = args.channelGroups,
channelGroups = _args$channelGroups === void 0 ? [] : _args$channelGroups;
channels.forEach(function (channel) {
if (channel in _this._channels) _this._channels[channel].state = state;
});
channelGroups.forEach(function (channelGroup) {
if (channelGroup in _this._channelGroups) {
_this._channelGroups[channelGroup].state = state;
}
});
return this._setStateEndpoint({
state: state,
channels: channels,
channelGroups: channelGroups
}, callback);
}
}, {
key: "adaptPresenceChange",
value: function adaptPresenceChange(args) {
var _this2 = this;
var connected = args.connected,
_args$channels2 = args.channels,
channels = _args$channels2 === void 0 ? [] : _args$channels2,
_args$channelGroups2 = args.channelGroups,
channelGroups = _args$channelGroups2 === void 0 ? [] : _args$channelGroups2;
if (connected) {
channels.forEach(function (channel) {
_this2._heartbeatChannels[channel] = {
state: {}
};
});
channelGroups.forEach(function (channelGroup) {
_this2._heartbeatChannelGroups[channelGroup] = {
state: {}
};
});
} else {
channels.forEach(function (channel) {
if (channel in _this2._heartbeatChannels) {
delete _this2._heartbeatChannels[channel];
}
});
channelGroups.forEach(function (channelGroup) {
if (channelGroup in _this2._heartbeatChannelGroups) {
delete _this2._heartbeatChannelGroups[channelGroup];
}
});
if (this._config.suppressLeaveEvents === false) {
this._leaveEndpoint({
channels: channels,
channelGroups: channelGroups
}, function (status) {
_this2._listenerManager.announceStatus(status);
});
}
}
this.reconnect();
}
}, {
key: "adaptSubscribeChange",
value: function adaptSubscribeChange(args) {
var _this3 = this;
var timetoken = args.timetoken,
_args$channels3 = args.channels,
channels = _args$channels3 === void 0 ? [] : _args$channels3,
_args$channelGroups3 = args.channelGroups,
channelGroups = _args$channelGroups3 === void 0 ? [] : _args$channelGroups3,
_args$withPresence = args.withPresence,
withPresence = _args$withPresence === void 0 ? false : _args$withPresence,
_args$withHeartbeats = args.withHeartbeats,
withHeartbeats = _args$withHeartbeats === void 0 ? false : _args$withHeartbeats;
if (!this._config.subscribeKey || this._config.subscribeKey === '') {
if (console && console.log) {
console.log('subscribe key missing; aborting subscribe');
}
return;
}
if (timetoken) {
this._lastTimetoken = this._currentTimetoken;
this._currentTimetoken = timetoken;
}
if (this._currentTimetoken !== '0' && this._currentTimetoken !== 0) {
this._storedTimetoken = this._currentTimetoken;
this._currentTimetoken = 0;
}
channels.forEach(function (channel) {
_this3._channels[channel] = {
state: {}
};
if (withPresence) _this3._presenceChannels[channel] = {};
if (withHeartbeats || _this3._config.getHeartbeatInterval()) _this3._heartbeatChannels[channel] = {};
_this3._pendingChannelSubscriptions.push(channel);
});
channelGroups.forEach(function (channelGroup) {
_this3._channelGroups[channelGroup] = {
state: {}
};
if (withPresence) _this3._presenceChannelGroups[channelGroup] = {};
if (withHeartbeats || _this3._config.getHeartbeatInterval()) _this3._heartbeatChannelGroups[channelGroup] = {};
_this3._pendingChannelGroupSubscriptions.push(channelGroup);
});
this._subscriptionStatusAnnounced = false;
this.reconnect();
}
}, {
key: "adaptUnsubscribeChange",
value: function adaptUnsubscribeChange(args, isOffline) {
var _this4 = this;
var _args$channels4 = args.channels,
channels = _args$channels4 === void 0 ? [] : _args$channels4,
_args$channelGroups4 = args.channelGroups,
channelGroups = _args$channelGroups4 === void 0 ? [] : _args$channelGroups4;
var actualChannels = [];
var actualChannelGroups = [];
channels.forEach(function (channel) {
if (channel in _this4._channels) {
delete _this4._channels[channel];
actualChannels.push(channel);
if (channel in _this4._heartbeatChannels) {
delete _this4._heartbeatChannels[channel];
}
}
if (channel in _this4._presenceChannels) {
delete _this4._presenceChannels[channel];
actualChannels.push(channel);
}
});
channelGroups.forEach(function (channelGroup) {
if (channelGroup in _this4._channelGroups) {
delete _this4._channelGroups[channelGroup];
actualChannelGroups.push(channelGroup);
if (channelGroup in _this4._heartbeatChannelGroups) {
delete _this4._heartbeatChannelGroups[channelGroup];
}
}
if (channelGroup in _this4._presenceChannelGroups) {
delete _this4._channelGroups[channelGroup];
actualChannelGroups.push(channelGroup);
}
});
if (actualChannels.length === 0 && actualChannelGroups.length === 0) {
return;
}
if (this._config.suppressLeaveEvents === false && !isOffline) {
this._leaveEndpoint({
channels: actualChannels,
channelGroups: actualChannelGroups
}, function (status) {
status.affectedChannels = actualChannels;
status.affectedChannelGroups = actualChannelGroups;
status.currentTimetoken = _this4._currentTimetoken;
status.lastTimetoken = _this4._lastTimetoken;
_this4._listenerManager.announceStatus(status);
});
}
if (Object.keys(this._channels).length === 0 && Object.keys(this._presenceChannels).length === 0 && Object.keys(this._channelGroups).length === 0 && Object.keys(this._presenceChannelGroups).length === 0) {
this._lastTimetoken = 0;
this._currentTimetoken = 0;
this._storedTimetoken = null;
this._region = null;
this._reconnectionManager.stopPolling();
}
this.reconnect();
}
}, {
key: "unsubscribeAll",
value: function unsubscribeAll(isOffline) {
this.adaptUnsubscribeChange({
channels: this.getSubscribedChannels(),
channelGroups: this.getSubscribedChannelGroups()
}, isOffline);
}
}, {
key: "getHeartbeatChannels",
value: function getHeartbeatChannels() {
return Object.keys(this._heartbeatChannels);
}
}, {
key: "getHeartbeatChannelGroups",
value: function getHeartbeatChannelGroups() {
return Object.keys(this._heartbeatChannelGroups);
}
}, {
key: "getSubscribedChannels",
value: function getSubscribedChannels() {
return Object.keys(this._channels);
}
}, {
key: "getSubscribedChannelGroups",
value: function getSubscribedChannelGroups() {
return Object.keys(this._channelGroups);
}
}, {
key: "reconnect",
value: function reconnect() {
this._startSubscribeLoop();
this._registerHeartbeatTimer();
}
}, {
key: "disconnect",
value: function disconnect() {
this._stopSubscribeLoop();
this._stopHeartbeatTimer();
this._reconnectionManager.stopPolling();
}
}, {
key: "_registerHeartbeatTimer",
value: function _registerHeartbeatTimer() {
this._stopHeartbeatTimer();
if (this._config.getHeartbeatInterval() === 0 || this._config.getHeartbeatInterval() === undefined) {
return;
}
this._performHeartbeatLoop();
this._heartbeatTimer = setInterval(this._performHeartbeatLoop.bind(this), this._config.getHeartbeatInterval() * 1000);
}
}, {
key: "_stopHeartbeatTimer",
value: function _stopHeartbeatTimer() {
if (this._heartbeatTimer) {
clearInterval(this._heartbeatTimer);
this._heartbeatTimer = null;
}
}
}, {
key: "_performHeartbeatLoop",
value: function _performHeartbeatLoop() {
var _this5 = this;
var heartbeatChannels = this.getHeartbeatChannels();
var heartbeatChannelGroups = this.getHeartbeatChannelGroups();
var presenceState = {};
if (heartbeatChannels.length === 0 && heartbeatChannelGroups.length === 0) {
return;
}
this.getSubscribedChannels().forEach(function (channel) {
var channelState = _this5._channels[channel].state;
if (Object.keys(channelState).length) {
presenceState[channel] = channelState;
}
});
this.getSubscribedChannelGroups().forEach(function (channelGroup) {
var channelGroupState = _this5._channelGroups[channelGroup].state;
if (Object.keys(channelGroupState).length) {
presenceState[channelGroup] = channelGroupState;
}
});
var onHeartbeat = function onHeartbeat(status) {
if (status.error && _this5._config.announceFailedHeartbeats) {
_this5._listenerManager.announceStatus(status);
}
if (status.error && _this5._config.autoNetworkDetection && _this5._isOnline) {
_this5._isOnline = false;
_this5.disconnect();
_this5._listenerManager.announceNetworkDown();
_this5.reconnect();
}
if (!status.error && _this5._config.announceSuccessfulHeartbeats) {
_this5._listenerManager.announceStatus(status);
}
};
this._heartbeatEndpoint({
channels: heartbeatChannels,
channelGroups: heartbeatChannelGroups,
state: presenceState
}, onHeartbeat.bind(this));
}
}, {
key: "_startSubscribeLoop",
value: function _startSubscribeLoop() {
var _this6 = this;
this._stopSubscribeLoop();
var presenceState = {};
var channels = [];
var channelGroups = [];
Object.keys(this._channels).forEach(function (channel) {
var channelState = _this6._channels[channel].state;
if (Object.keys(channelState).length) {
presenceState[channel] = channelState;
}
channels.push(channel);
});
Object.keys(this._presenceChannels).forEach(function (channel) {
channels.push("".concat(channel, "-pnpres"));
});
Object.keys(this._channelGroups).forEach(function (channelGroup) {
var channelGroupState = _this6._channelGroups[channelGroup].state;
if (Object.keys(channelGroupState).length) {
presenceState[channelGroup] = channelGroupState;
}
channelGroups.push(channelGroup);
});
Object.keys(this._presenceChannelGroups).forEach(function (channelGroup) {
channelGroups.push("".concat(channelGroup, "-pnpres"));
});
if (channels.length === 0 && channelGroups.length === 0) {
return;
}
var subscribeArgs = {
channels: channels,
channelGroups: channelGroups,
state: presenceState,
timetoken: this._currentTimetoken,
filterExpression: this._config.filterExpression,
region: this._region
};
this._subscribeCall = this._subscribeEndpoint(subscribeArgs, this._processSubscribeResponse.bind(this));
}
}, {
key: "_processSubscribeResponse",
value: function _processSubscribeResponse(status, payload) {
var _this7 = this;
if (status.error) {
if (status.category === _categories["default"].PNTimeoutCategory) {
this._startSubscribeLoop();
} else if (status.category === _categories["default"].PNNetworkIssuesCategory) {
this.disconnect();
if (status.error && this._config.autoNetworkDetection && this._isOnline) {
this._isOnline = false;
this._listenerManager.announceNetworkDown();
}
this._reconnectionManager.onReconnection(function () {
if (_this7._config.autoNetworkDetection && !_this7._isOnline) {
_this7._isOnline = true;
_this7._listenerManager.announceNetworkUp();
}
_this7.reconnect();
_this7._subscriptionStatusAnnounced = true;
var reconnectedAnnounce = {
category: _categories["default"].PNReconnectedCategory,
operation: status.operation,
lastTimetoken: _this7._lastTimetoken,
currentTimetoken: _this7._currentTimetoken
};
_this7._listenerManager.announceStatus(reconnectedAnnounce);
});
this._reconnectionManager.startPolling();
this._listenerManager.announceStatus(status);
} else if (status.category === _categories["default"].PNBadRequestCategory) {
this._stopHeartbeatTimer();
this._listenerManager.announceStatus(status);
} else {
this._listenerManager.announceStatus(status);
}
return;
}
if (this._storedTimetoken) {
this._currentTimetoken = this._storedTimetoken;
this._storedTimetoken = null;
} else {
this._lastTimetoken = this._currentTimetoken;
this._currentTimetoken = payload.metadata.timetoken;
}
if (!this._subscriptionStatusAnnounced) {
var connectedAnnounce = {};
connectedAnnounce.category = _categories["default"].PNConnectedCategory;
connectedAnnounce.operation = status.operation;
connectedAnnounce.affectedChannels = this._pendingChannelSubscriptions;
connectedAnnounce.subscribedChannels = this.getSubscribedChannels();
connectedAnnounce.affectedChannelGroups = this._pendingChannelGroupSubscriptions;
connectedAnnounce.lastTimetoken = this._lastTimetoken;
connectedAnnounce.currentTimetoken = this._currentTimetoken;
this._subscriptionStatusAnnounced = true;
this._listenerManager.announceStatus(connectedAnnounce);
this._pendingChannelSubscriptions = [];
this._pendingChannelGroupSubscriptions = [];
}
var messages = payload.messages || [];
var _this$_config = this._config,
requestMessageCountThreshold = _this$_config.requestMessageCountThreshold,
dedupeOnSubscribe = _this$_config.dedupeOnSubscribe;
if (requestMessageCountThreshold && messages.length >= requestMessageCountThreshold) {
var countAnnouncement = {};
countAnnouncement.category = _categories["default"].PNRequestMessageCountExceededCategory;
countAnnouncement.operation = status.operation;
this._listenerManager.announceStatus(countAnnouncement);
}
messages.forEach(function (message) {
var channel = message.channel;
var subscriptionMatch = message.subscriptionMatch;
var publishMetaData = message.publishMetaData;
if (channel === subscriptionMatch) {
subscriptionMatch = null;
}
if (dedupeOnSubscribe) {
if (_this7._dedupingManager.isDuplicate(message)) {
return;
} else {
_this7._dedupingManager.addEntry(message);
}
}
if (_utils["default"].endsWith(message.channel, '-pnpres')) {
var announce = {};
announce.channel = null;
announce.subscription = null;
announce.actualChannel = subscriptionMatch != null ? channel : null;
announce.subscribedChannel = subscriptionMatch != null ? subscriptionMatch : channel;
if (channel) {
announce.channel = channel.substring(0, channel.lastIndexOf('-pnpres'));
}
if (subscriptionMatch) {
announce.subscription = subscriptionMatch.substring(0, subscriptionMatch.lastIndexOf('-pnpres'));
}
announce.action = message.payload.action;
announce.state = message.payload.data;
announce.timetoken = publishMetaData.publishTimetoken;
announce.occupancy = message.payload.occupancy;
announce.uuid = message.payload.uuid;
announce.timestamp = message.payload.timestamp;
if (message.payload.join) {
announce.join = message.payload.join;
}
if (message.payload.leave) {
announce.leave = message.payload.leave;
}
if (message.payload.timeout) {
announce.timeout = message.payload.timeout;
}
_this7._listenerManager.announcePresence(announce);
} else if (message.messageType === 1) {
var _announce = {};
_announce.channel = null;
_announce.subscription = null;
_announce.channel = channel;
_announce.subscription = subscriptionMatch;
_announce.timetoken = publishMetaData.publishTimetoken;
_announce.publisher = message.issuingClientId;
if (message.userMetadata) {
_announce.userMetadata = message.userMetadata;
}
_announce.message = message.payload;
_this7._listenerManager.announceSignal(_announce);
} else if (message.messageType === 2) {
var _announce2 = {};
_announce2.channel = null;
_announce2.subscription = null;
_announce2.channel = channel;
_announce2.subscription = subscriptionMatch;
_announce2.timetoken = publishMetaData.publishTimetoken;
_announce2.publisher = message.issuingClientId;
if (message.userMetadata) {
_announce2.userMetadata = message.userMetadata;
}
_announce2.message = {
event: message.payload.event,
type: message.payload.type,
data: message.payload.data
};
_this7._listenerManager.announceObjects(_announce2);
if (message.payload.type === 'user') {
_this7._listenerManager.announceUser(_announce2);
} else if (message.payload.type === 'space') {
_this7._listenerManager.announceSpace(_announce2);
} else if (message.payload.type === 'membership') {
_this7._listenerManager.announceMembership(_announce2);
}
} else if (message.messageType === 3) {
var _announce3 = {};
_announce3.channel = channel;
_announce3.subscription = subscriptionMatch;
_announce3.timetoken = publishMetaData.publishTimetoken;
_announce3.publisher = message.issuingClientId;
_announce3.data = {
messageTimetoken: message.payload.data.messageTimetoken,
actionTimetoken: message.payload.data.actionTimetoken,
type: message.payload.data.type,
uuid: message.issuingClientId,
value: message.payload.data.value
};
_announce3.event = message.payload.event;
_this7._listenerManager.announceMessageAction(_announce3);
} else if (message.messageType === 4) {
var _announce4 = {};
_announce4.channel = channel;
_announce4.subscription = subscriptionMatch;
_announce4.timetoken = publishMetaData.publishTimetoken;
_announce4.publisher = message.issuingClientId;
var msgPayload = message.payload;
if (_this7._config.cipherKey) {
var decryptedPayload = _this7._crypto.decrypt(message.payload);
if ((0, _typeof2["default"])(decryptedPayload) === 'object' && decryptedPayload !== null) {
msgPayload = decryptedPayload;
}
}
if (message.userMetadata) {
_announce4.userMetadata = message.userMetadata;
}
_announce4.message = msgPayload.message;
_announce4.file = {
id: msgPayload.file.id,
name: msgPayload.file.name,
url: _this7._getFileUrl({
id: msgPayload.file.id,
name: msgPayload.file.name,
channel: channel
})
};
_this7._listenerManager.announceFile(_announce4);
} else {
var _announce5 = {};
_announce5.channel = null;
_announce5.subscription = null;
_announce5.actualChannel = subscriptionMatch != null ? channel : null;
_announce5.subscribedChannel = subscriptionMatch != null ? subscriptionMatch : channel;
_announce5.channel = channel;
_announce5.subscription = subscriptionMatch;
_announce5.timetoken = publishMetaData.publishTimetoken;
_announce5.publisher = message.issuingClientId;
if (message.userMetadata) {
_announce5.userMetadata = message.userMetadata;
}
if (_this7._config.cipherKey) {
_announce5.message = _this7._crypto.decrypt(message.payload);
} else {
_announce5.message = message.payload;
}
_this7._listenerManager.announceMessage(_announce5);
}
});
this._region = payload.metadata.region;
this._startSubscribeLoop();
}
}, {
key: "_stopSubscribeLoop",
value: function _stopSubscribeLoop() {
if (this._subscribeCall) {
if (typeof this._subscribeCall.abort === 'function') {
this._subscribeCall.abort();
}
this._subscribeCall = null;
}
}
}]);
return _default;
}();
exports["default"] = _default;
module.exports = exports.default;
//# sourceMappingURL=subscription_manager.js.map