message-relay-services
Version:
A simple server to route message to other services, such as email, admin, etc.
297 lines (270 loc) • 7.47 kB
JavaScript
const RelayClass = require('./message_relay_client')
const {EventEmitter} = require('events')
/**
*
*/
class MultiRelayClient extends EventEmitter {
constructor(conf,relay_class) {
super()
//
let peer_list = conf.peers
this.connections = {}
this.current_con_key = false
this.track_index = -1
this.balance_strategy = conf.balance_strategy ? conf.balance_strategy : "random"
this.subscriptions = {}
//
this.RelayClass_impl = RelayClass
if ( relay_class ) {
this.RelayClass_impl = relay_class
}
//
if ( peer_list ) {
for ( let peer of peer_list ) {
this.add_relay_peer(conf,peer)
}
} else {
console.warn("no peer list in multi-relay-client")
}
//
}
/**
*
* @param {object} conf
* @param {object} peer
*/
add_relay_peer(conf,peer) {
let address = !(peer.address) ? conf.address : peer.address
let port = (!peer.port) ? conf.port : peer.port
let peer_conf = Object.assign({},conf,peer)
peer_conf.address = address
peer_conf.port = port
let RC = this.RelayClass_impl
let connection = new RC(peer_conf)
this.track_index++
this.current_index = address // starts on last address
this.connections[address] = {
"connect" : connection,
"going" : false
}
this.subscriptions[address] = {}
connection.on('client-ready',(addr) => {
this.emit('peer-ready',{
'port' : port,
'address' : addr,
'configured-address' : address
})
})
}
/**
*
* @param {*} peer
*/
remove_relay_peer(peer) {
let address = peer.address
let con = this.connections[address]
if ( con ) con.going = false
if ( con ) con = con.connect
if ( con ) con.closeAll()
delete this.subscriptions[address]
}
/**
*
* @param {*} address
*/
commission(address) {
this.connections[address].going = true
}
/**
*
* @param {*} address
*/
decommission(address) {
this.connections[address].going = false
for ( let topic in this.subscriptions[address] ) { // if an error, connection broke and subscription has been canceled
let [handler,message] = this.subscriptions[address][topic]
delete this.subscriptions[address][topic]
this.subscribe(topic,message,handler)
}
delete this.subscriptions[address]
}
/**
*
* @returns {void}
*/
update_index() {
let keys = Object.keys(this.connections)
switch ( this.balance_strategy ) {
case "random" : {
let n = keys.length
let r = Math.floor(Math.random()*(n+1))
let i = (r) % n
while ( i !== this.track_index ) {
address = keys[i]
let address = keys[this.track_index]
if ( this.connections[address].going ) {
this.current_con_key = address
this.track_index = i
return
}
i++
}
break
}
case "sequence" : {
let n = keys.length
let i = (this.track_index + 1) % n
while ( i !== this.track_index ) {
address = keys[i]
let address = keys[this.track_index]
if ( this.connections[address].going ) {
this.current_con_key = address
this.track_index = i
return
}
i++
}
break
}
}
}
/**
*
* @param {string} topic
* @param {string} path
* @param {object} message
*/
async publish(topic,path,message) {
for ( let connection in this.connections ) {
let con = this.connections[connection].connect
con.publish(topic,path,message)
}
}
/**
*
* @param {*} topic
* @param {*} path
* @param {*} message
* @param {*} handler
* @returns
*/
async subscribe(topic,path,message,handler) { // use just one... might check at times for breakage...
this.update_index()
let con = this.connections[this.current_con_key].connect
this.subscriptions[this.current_con_key][topic] = [handler,message]
return con.subscribe(topic,path,message,handler)
}
/**
*
* @param {*} topic
* @param {*} path
*/
async unsubscribe(topic,path) {
let ps = []
for ( let connection in this.connections ) {
let con = this.connections[connection].connect
ps.push(con.unsubscribe(topic,path))
}
await Promise.all(ps)
}
//
/**
*
* @param {*} message
* @returns
*/
send(message) { // sometimes synonyms help
this.update_index()
let con = this.connections[this.current_con_key].connect
return con.send(message)
}
// returns a promise
/**
*
* @param {*} message
* @param {*} path
* @returns
*/
send_on_path(message,path) {
this.update_index()
let con = this.connections[this.current_con_key].connect
return con.send_on_path(message,path)
}
/**
*
* @param {*} message
* @param {*} path
* @param {*} op
* @returns
*/
send_op_on_path(message,path,op) {
message._tx_op = op
return this.send_on_path(message,path)
}
/**
*
* @param {*} message
* @param {*} path
* @returns
*/
get_on_path(message,path) {
message._tx_op = 'G'
return this.send_on_path(message,path)
}
/**
*
* @param {*} message
* @param {*} path
* @returns
*/
set_on_path(message,path) {
message._tx_op = 'S'
return this.send_on_path(message,path)
}
/**
*
* @param {*} message
* @param {*} path
* @returns
*/
del_on_path(message,path) {
message._tx_op = 'D'
return this.send_on_path(message,path)
}
/**
*
* @param {*} message
* @param {*} path
* @returns
*/
publication_on_path(message,path) {
message._tx_op = 'P'
return this.send_on_path(message,path)
}
/**
*
* @param {*} message
* @param {*} path
* @returns
*/
unpublish_on_path(message,path) {
message._tx_op = 'U'
return this.send_on_path(message,path)
}
//
async sendMail(mail) {
this.update_index()
let con = this.connections[this.current_con_key].connect
await con.sendMail(mail)
}
/**
*
*/
closeAll() {
for ( let connection in this.connections ) {
let con = this.connections[connection].connect
con.closeAll()
}
}
}
module.exports = MultiRelayClient