processing.js
  added default ready command for socket
  removed arrow functions from socket cmd functions must Always be regular functions for setting context
base.js
  better validation and defaults for options to registerSocket
  add by default a ready observer for when socket starts listening
master
David Kebler 2020-01-14 09:20:44 -08:00
parent a47144606a
commit a6d928bf56
3 changed files with 47 additions and 35 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@uci/base", "name": "@uci/base",
"version": "0.1.37", "version": "0.1.38",
"description": "Multi type and transport JSON packet communication base class. Used in UCI extended classes", "description": "Multi type and transport JSON packet communication base class. Used in UCI extended classes",
"main": "src/base", "main": "src/base",
"scripts": { "scripts": {

View File

@ -62,7 +62,8 @@ const TRANSLATE = {
class Base extends EventEmitter { class Base extends EventEmitter {
constructor(opts = {}) { constructor(opts = {}) {
super() super()
this.id = opts.id || opts.name || 'uci-base:' + new Date().getTime() this.name = opts.name || opts.appName || 'a base class instance'
this.id = opts.id || 'uci-base:' + new Date().getTime()
log = logger({ name: 'base', id: this.id }) log = logger({ name: 'base', id: this.id })
this.desc = opts.desc // additional details for humans this.desc = opts.desc // additional details for humans
this._socket = {} // holds all the various communication sockets this._socket = {} // holds all the various communication sockets
@ -71,37 +72,31 @@ class Base extends EventEmitter {
this.retryWait = opts.retryWait this.retryWait = opts.retryWait
this.defaultReturnCmd = opts.defaultReturnCmd this.defaultReturnCmd = opts.defaultReturnCmd
this._cmdProcessors = { _default: cmdProcessor } this._cmdProcessors = { _default: cmdProcessor }
this.ready = new Ready({emitter: this})
// _c and _s are the default namespaces // _c and _s are the default namespaces
this._namespaces =Object.assign({},namespaces) this._namespaces =Object.assign({},namespaces)
this._c = Object.assign({},defaultCmds.c) this._c = Object.assign({},defaultCmds.c)
this._s = Object.assign({},defaultCmds.s) this._s = Object.assign({},defaultCmds.s)
// make available a method that will bind a whole object tree of functions
// Note: functions called via a command namespace are called by base connext by default
// if called directlly/locally they should be bound to base context if desired
this.bindFuncs = bindFuncs
if (opts.useRootNS) { if (opts.useRootNS) {
// add root of instance to checking for command functions - not recommended! // add root of instance to checking for command functions - not recommended!
this._namespaces.s.splice(-1, 0, null) this._namespaces.s.splice(-1, 0, null)
this._namespaces.c.splice(-1, 0, null) this._namespaces.c.splice(-1, 0, null)
} }
// method that will bind a whole object tree of functions // this.bindFuncs = bindFuncs // make available a method that will bind a whole object tree of functions
this.bindFuncs = bindFuncs this._socket = {} // where all sockets are stored
// predefined sockets: // at creation defined sockets:
// comma delimited list of this form '<name>#<c/p/s>><n=np/t=tcp/m=mqtt/w=web>' if (opts.port) this.registerSocket(`${opts.name||'base'}${opts.port&&opts.path ? ':t':''}`,'s','t',{port:opts.port})
this._socket = {} if (opts.path) this.registerSocket(`${opts.name||'base'}${opts.port&&opts.path ? ':n':''}`,'s','n',{path: opts.path})
if (opts.sockets) { if (opts.sockets) {
opts.sockets.split(/[,|\s]+/).forEach(socketStr => { let sockets = opts.sockets
let socket = {} sockets = Array.isArray(sockets) ? sockets:[sockets]
socketStr.split(/[>#]+/).map(function(prop, index) { sockets.forEach(socket => this.registerSocket(socket))
socket[SOCKET_INFO_KEYS[index]] = prop
})
this.registerSocket(
socket.name,
socket.type,
socket.transport,
opts[socket.name]
)
})
} }
this.ready = new Ready({emitter: this, verbose:process.env.UCI_READY_VERBOSE})
} // end constructor } // end constructor
/* /*
@ -187,16 +182,23 @@ class Base extends EventEmitter {
* @returns {function} if called before base initialzation it can be ignored as all added sockets will be initialized. After through it be called to initialize that socket * @returns {function} if called before base initialzation it can be ignored as all added sockets will be initialized. After through it be called to initialize that socket
*/ */
registerSocket(name, type = 'c', transport = 'n', options = {}) { registerSocket(name, type = 'c', transport = 'n', options = {}) {
// console.log('=========================================REGISTER=========',name)
if (isPlainObject(name)) ({name, type = 'c', transport = 'n', options = {}} = name)
if (typeof name !=='string') return null
transport = this._validateTransport(transport) transport = this._validateTransport(transport)
log.debug({ socketName: name, type: type, tranport: transport, options: options, method:'addSocket', line:147, msg:`adding socket ${name}`}) // console.log({ socketName: name, type: type, tranport: transport, options: options, method:'addSocket', line:198, msg:`adding socket ${name}`})
options.id = options.id || this.id + ':' + name options.id = options.id || name
if (type==='c') options = Object.assign({initTimeout:this.initTimeout, retryWait:this.retryWait},options) options.name = options.name || name
if (type==='s') options = Object.assign({defaultReturnCmd:this.defaultReturnCmd},options) // TODO add a 'd' type for duplex which creates an 's' first and waits on connect to make a 'c'
if (type==='c') options = Object.assign({initTimeout:this.initTimeout, retryWait:this.retryWait},options) // outbound
if (type==='s') options = Object.assign({defaultReturnCmd:this.defaultReturnCmd},options) // inbound
// TODO get rid of hard coded transports and use registered transports (t and n being default)
switch (transport) { switch (transport) {
case 'n': case 'n':
options.path = options.path || true options.path = options.path || true
// falls through // falls through
case 't': case 't':
// console.log('==========socket options==========\n',name,type,transport,options)
this._socket[name] = new Socket[TRANSLATE[type]](options) this._socket[name] = new Socket[TRANSLATE[type]](options)
break break
case 'm': case 'm':
@ -232,16 +234,20 @@ class Base extends EventEmitter {
this.emit(event,obj) this.emit(event,obj)
}) })
}) })
if (type==='c') { if (type==='c') {
this.ready.addObserver(name,this._socket[name],{event:'connection:socket',condition:ev=>{return ev.state==='connected'}})
this.ready.addObserver(name,this.getSocket(name),{event:'connection:socket',condition:ev=>{return ev.state==='connected'}})
this._socket[name].on('pushed', packet => { this._socket[name].on('pushed', packet => {
packet._header.socketName=name packet._header.socketName=name
this.emit('pushed', packet) this.emit('pushed', packet)
}) })
} }
if (type==='s') {
// this._socket[name].on('socket',ev=>console.log(ev))
this.ready.addObserver(name,this._socket[name],{ event:'socket', condition: ev => (ev || {}).state ==='listening' })
}
return this.getSocketInit(name) // returns the init function (e.g. connect or create) for the socket return this.getSocketInit(name) // returns the init function (e.g. connect or create) for the socket
} }
} }
@ -634,12 +640,12 @@ class Base extends EventEmitter {
packet.cmd, packet.cmd,
this._namespaces[this._type(socket) + this._transport(socket)] this._namespaces[this._type(socket) + this._transport(socket)]
) )
if (cmd_func) return await cmd_func.bind(this)(packet) // todo try .call if (cmd_func) return await cmd_func.call(this,packet) // todo try .call
cmd_func = this._getCmdFuncNamespace( cmd_func = this._getCmdFuncNamespace(
packet.cmd, packet.cmd,
this._namespaces[this._type(socket)] this._namespaces[this._type(socket)]
) )
if (cmd_func) return await cmd_func.bind(this)(packet) if (cmd_func) return await cmd_func.call(this,packet)
return 'failed' return 'failed'
} }

View File

@ -10,7 +10,7 @@ let log = logger({ package: 'base',file:'processing.js'})
// common processor, will call based on type s or c the ones below // common processor, will call based on type s or c the ones below
const cmdProcessor = async function (packet,socket) { const cmdProcessor = async function (packet,socket) {
let [err,res] = await to(_process[this.getSocket(socket).type].bind(this)(packet,socket)) let [err,res] = await to(_process[this.getSocket(socket).type].call(this,packet,socket))
if (err) { if (err) {
let error = {cmd:'error', error:err, packet:packet, socket:socket, function:'processor', line: 15, msg:`'unhandled error in packet command function ${packet.cmd}`} let error = {cmd:'error', error:err, packet:packet, socket:socket, function:'processor', line: 15, msg:`'unhandled error in packet command function ${packet.cmd}`}
log.error(error) log.error(error)
@ -21,8 +21,6 @@ const cmdProcessor = async function (packet,socket) {
return res return res
} }
export { cmdProcessor, defaultCmds, namespaces }
// default processors for socket/server and consumer/client // default processors for socket/server and consumer/client
const _process = { const _process = {
s: async function (packet,socket) { s: async function (packet,socket) {
@ -64,17 +62,23 @@ const namespaces = {
const defaultCmds ={ const defaultCmds ={
s:{ s:{
echo: async packet => { echo: function (packet) {
packet.processed = true packet.processed = true
packet.msg = 'default socket echo' packet.msg = 'default socket echo'
return packet return packet
}, },
// add sedning along an ack to any consumers and or pushing to other sockets on this device // add sedning along an ack to any consumers and or pushing to other sockets on this device
ack: async packet => { ack: async function (packet) {
packet.cmd = 'reply' packet.cmd = 'reply'
packet.ack = true packet.ack = true
packet.msg = 'this is the base default ack, superceed in your instance or extended class' packet.msg = 'this is the base default ack, superceed in your instance or extended class'
return packet return packet
},
ready: async function (packet) {
console.log('=========================READY RECEIVED AND EMITTED ======================================\n',packet)
packet.msg = 'ready state was emitted on receiving process'
this.emit(packet.event || packet.name || packet.id, !!(packet.ready || packet.value || packet.state))
return packet
} }
}, },
c:{ c:{
@ -88,3 +92,5 @@ const defaultCmds ={
} }
} }
} }
export { cmdProcessor, defaultCmds, namespaces }