/**
* @module flitter-socket/Transaction
*/
const uuid = require('uuid/v4')
/**
* Object representing one exchange between two parties in
* a websocket connection. Handles response sending and request
* data.
* @class
*/
class Transaction {
constructor(data, connection_manager){
/**
* Whether the transaction has completed. That is, has the
* recipient sent a response.
* @type {boolean}
*/
this.resolved = false
/**
* Universally-unique id of this transaction. Used by both
* parties to match up requests and responses.
* @type {string}
*/
this.id = data.transaction_id ? data.transaction_id : uuid()
/**
* The open websocket used for communication
* @type {Socket}
*/
this.socket = connection_manager.socket
/**
* Contains the outgoing data for the transaction.
* That is, the data sent/to be sent back to the sender.
* @type {Object}
*/
this.outgoing = data.outgoing ? data.outgoing : {}
/**
* Contains the incoming data for the transaction.
* That is, the data from the sender.
* @type {Object}
*/
this.incoming = data.incoming ? data.incoming : {}
/**
* Unique ID of the websocket client's connection in the connection manager.
* @type {string}
*/
this.connection_id = connection_manager.id
/**
* Is this a request or a response-type transaction?
* @type {"request"|"response"}
*/
this.type = data.type ? data.type : 'request'
/**
* Set to true if 1. this transaction is a response and 2. the response has been sent
* @type {boolean}
*/
this.sent = false
/**
* Set to true if 1. the transaction was awaiting data and 2. the data has been received
* @type {boolean}
*/
this.received = false
/**
* HTTP-equivalent status code for this transaction. Use this.status() to get/set.
* @type {number}
* @private
*/
this._status = 200
/**
* Message to be included in the response. User this.message() to get/set.
* @type {string}
* @private
*/
this._message = ""
/**
* The connection manager that spawned this transaction.
* @type {module:flitter-socket/ConnectionManager~ConnectionManager}
*/
this.cm = connection_manager
}
/**
* Mark the transaction as resolved.
*/
resolve(){
this.resolved = true
}
/**
* Get or set the status code. If a new code is provided, set the status code and return
* this for chaining. Otherwise, get the current status code.
* @param {number} [code]
* @returns {Transaction|number} - if code is provided, set the code and return this; otherwise return the current code
*/
status(code = null){
if ( code ){
this._status = code
return this
}
return this._status
}
/**
* Get or set the message. If a new message is provided, set the message and return
* this for chaining. Otherwise get the current message.
* @param {string} [msg]
* @returns {Transaction|string} - if message is provided, set the message and return this; otherwise return the current message
*/
message(msg = null){
if ( msg ){
this._message = msg
return this
}
return this._message
}
/**
* Send data to the recipient of this transaction as stringified JSON, and resolve the transaction.
* @param data
* @returns {*|boolean|void}
*/
send(data){
if ( this.type === 'request' ){
if ( this.resolved ) throw new Error(`Transaction can only be sent once per request. (ID: ${this.id})`)
const obj = {
status: this._status,
transaction_id: this.id,
type: 'response',
... (this._message && {message: this._message}),
... (data && {data}),
... ((!data && this.outgoing) && {data: this.outgoing})
}
this.json = JSON.stringify(obj)
this.resolve()
}
else if ( this.type === 'response' ){
if ( this.sent ) throw new Error(`Request can only be sent once per Transaction. (ID: ${this.id})`)
const obj = {
endpoint: this.endpoint,
transaction_id: this.id,
type: 'request',
... (data && {data}),
... ((!data && this.outgoing) && {data: this.outgoing})
}
this.json = JSON.stringify(obj)
}
this.sent = true
return this.socket.send(this.json)
}
}
module.exports = exports = Transaction