@pandorajs/hub
Version:
pandora.js messenge hub
317 lines • 10.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HubClient = void 0;
const uuid = require("uuid");
const messenger_1 = require("@pandorajs/messenger");
const const_1 = require("../const");
const SelectorUtils_1 = require("./SelectorUtils");
const util_1 = require("util");
const DefaultDispatchHandler_1 = require("./DefaultDispatchHandler");
const events_1 = require("events");
class HubClient extends events_1.EventEmitter {
constructor(options) {
super();
this.messengerClient = null;
this.publishedSelectors = [];
this.location = {
...options.location,
clientId: uuid.v4(),
};
this.logger = options.logger || console;
this.dispatchHandlers = [new DefaultDispatchHandler_1.DefaultDispatchHandler()];
}
/**
* Set a handler to hand HUB Dispatching message
* @param {DispatchHandler} dispatchHandler
*/
pushDispatchHandler(dispatchHandler) {
this.dispatchHandlers.push(dispatchHandler);
}
async handleHubDispatch(message) {
for (const dispatchHandler of this.dispatchHandlers) {
const ret = await dispatchHandler.dispatch(message);
if (ret) {
return ret;
}
}
this.emit(message.action, message);
}
isReady() {
return !!this.messengerClient;
}
/**
* Let this client online
* @return {Promise<void>}
*/
async start() {
if (this.messengerClient) {
throw new Error('HubClient already started');
}
await new Promise((resolve, reject) => {
this.messengerClient = new messenger_1.MessengerClient({
name: const_1.HUB_SOCKET_NAME,
reConnectTimes: 10,
responseTimeout: const_1.TIMEOUT_OF_RESPONSE,
unref: true,
});
this.messengerClient.once('error', reject);
this.messengerClient.ready(resolve);
});
this.startListen();
await this.sendOnline();
// When reconnected
this.messengerClient.on('connect', () => {
this.resendPublishedSelectors().catch(err => {
this.logger.error(err);
this.logger.error('resendPublishedSelectors() went wrong');
});
});
}
async sendOnline() {
await this.sendToHubAndWaitReply(const_1.PANDORA_HUB_ACTION_ONLINE_UP);
}
/**
* Publish a selector to Hub, so Hub will set a relation in RouteTable between client and selector
* @param {Selector} selector
* @return {Promise<ReplyPackage>}
*/
async publish(selector) {
// Make sure each selector are unique.
this.assertExistSelector(selector);
const res = await this.sendPublishToHub(selector);
this.publishedSelectors.push(selector);
return res;
}
/**
* Unpublish a selector to Hub, so Hub will forget the relation in RouteTable between client and selector
* @param {Selector} selector
* @return {Promise<ReplyPackage>}
*/
async unpublish(selector) {
const filteredSelectors = [];
const batchReply = [];
for (const targetSelector of this.publishedSelectors) {
if (!SelectorUtils_1.SelectorUtils.match(selector, targetSelector)) {
filteredSelectors.push(targetSelector);
continue;
}
const res = await this.sendToHubAndWaitReply(const_1.PANDORA_HUB_ACTION_UNPUBLISH_UP, {
data: {
selector: targetSelector,
},
});
batchReply.push(res);
if (!res.success) {
throw new Error(util_1.format('Unpublish selector %j went wrong, cause from Hub: %s', selector, res.error));
}
}
this.publishedSelectors = filteredSelectors;
return {
success: true,
batchReply,
};
}
/**
* Resend all published selectors to HUB when reconnected
* @return {Promise<void>}
*/
async resendPublishedSelectors() {
await this.sendOnline();
for (const selector of this.publishedSelectors) {
await this.sendPublishToHub(selector);
}
}
// /**
// * Get all route relations within Hub
// * @return {Promise<any>}
// */
// async discover() {
// const res = await this.sendToHubAndWaitReply(PANDORA_HUB_ACTION_DISCOVER_UP);
// if(!res.success) {
// throw new Error(format('discover whole hub went wrong, cause from Hub: %j', res.error));
// }
// return res.data;
// }
//
// /**
// * Lookup route relations by a certain selector
// * @param {Selector} selector
// * @return {Promise<any>}
// */
// async lookup(selector: Selector) {
// const res = await this.sendToHubAndWaitReply<LookupPackage>(PANDORA_HUB_ACTION_DISCOVER_UP, {
// data: {
// selector: selector
// }
// });
// if(!res.success) {
// throw new Error(format('lookup selector %j went wrong, cause from Hub: %j', selector, res.error));
// }
// return res.data;
// }
/**
* Invoke a remote Object only from a random one of all selected clients
* @return {Promise<any>}
*/
async invoke(remote, action, message) {
const res = await this.sendToHubAndWaitReply(const_1.PANDORA_HUB_ACTION_MSG_UP, {
remote: remote,
action,
broadcast: false,
...message,
});
return res;
}
/**
* Invoke a remote Object from all selected clients
* @param {Selector} remote
* @param message
* @return {Promise<Array<ReplyPackage>>}
*/
async multipleInvoke(remote, action, message) {
const res = await this.sendToHubAndWaitReply(const_1.PANDORA_HUB_ACTION_MSG_UP, {
remote: remote,
action,
broadcast: true,
...message,
});
return res.batchReply;
}
/**
* Send a message to a random one of all selected clients
* @param remote
* @param data
* @return {Promise<void>}
*/
send(remote, action, message) {
this.sendToHub(const_1.PANDORA_HUB_ACTION_MSG_UP, {
remote: remote,
action,
broadcast: false,
...message,
});
}
/**
* Send a message to all selected clients
* @param remote
* @param message
* @return {Promise<void>}
*/
multipleSend(remote, action, message) {
this.sendToHub(const_1.PANDORA_HUB_ACTION_MSG_UP, {
remote: remote,
action,
broadcast: true,
...message,
});
}
/**
* Get location of this client
* @return {Location}
*/
getLocation() {
return this.location;
}
/**
* Send a message to Hub
*/
sendToHub(action, message) {
message = (message || {});
message.host = this.location;
this.messengerClient.send(action, message);
}
/**
* Send a message to Hub and wait reply
* @param action
* @param {MessageType} message
* @return {Promise<ReplyPackage>}
*/
async sendToHubAndWaitReply(action, message) {
message = (message || {});
message.host = this.location;
message.needReply = true;
return new Promise((resolve, reject) => {
this.messengerClient.send(action, message, (err, message) => {
if (err) {
reject(err);
return;
}
resolve(message);
}, message.timeout);
});
}
/**
* only send publish message to Hub without state keeping
* @param {Selector} selector
* @return {Promise<ReplyPackage>}
*/
async sendPublishToHub(selector) {
const res = await this.sendToHubAndWaitReply(const_1.PANDORA_HUB_ACTION_PUBLISH_UP, {
data: {
selector: selector,
},
});
if (!res.success) {
throw new Error(util_1.format('Publish selector %j went wrong, cause from Hub: %s', selector, res.error));
}
return res;
}
/**
* Make sure each selector are unique
* @param selector
*/
assertExistSelector(selector) {
for (const targetSelector of this.publishedSelectors) {
if (SelectorUtils_1.SelectorUtils.match(selector, targetSelector)) {
throw new Error(util_1.format('Selector %j already exist', selector));
}
}
}
startListen() {
this.messengerClient.on(const_1.PANDORA_HUB_ACTION_MSG_DOWN, async (message, reply) => {
try {
let replyPkg = null;
try {
const data = await this.handleHubDispatch(message);
replyPkg = {
host: this.location,
remote: message.host,
success: true,
data: data,
};
}
catch (error) {
replyPkg = {
host: this.location,
remote: message.host,
success: false,
error: error,
};
}
if (message.needReply) {
reply(replyPkg);
}
}
catch (err) {
this.logger.error(err);
this.logger.error(util_1.format('Handing PANDORA_HUB_ACTION_MSG_DOWN went wrong, remote message: %j', message));
}
});
}
/**
* Close this client
*/
async stop() {
if (!this.messengerClient) {
throw new Error('HubClient has not started yet');
}
await this.sendToHubAndWaitReply(const_1.PANDORA_HUB_ACTION_OFFLINE_UP);
this.messengerClient.close();
this.messengerClient = null;
}
getMessengerClient() {
return this.messengerClient;
}
}
exports.HubClient = HubClient;
//# sourceMappingURL=HubClient.js.map