'use strict';
/**
* @module lib/operations/messages
* @summary Whiteflag API messages endpoints handler module
* @description Module with api messages endpoint handlers
* @tutorial modules
* @tutorial openapi
*/
module.exports = {
// Endpoint handler functions
send,
receive,
validate,
encode,
decode,
getMessages,
getReferences,
getSequence
};
// Whiteflag common functions and classes //
const response = require('../common/httpres');
const array = require('../common/arrays');
const { ProcessingError } = require('../common/errors');
// Whiteflag modules //
const wfCodec = require('../protocol/codec');
const wfRetrieve = require('../protocol/retrieve');
const wfReference = require('../protocol/references');
// Whiteflag event emitters //
const wfRxEvent = require('../protocol/events').rxEvent;
const wfTxEvent = require('../protocol/events').txEvent;
// MAIN MODULE FUNCTIONS //
/**
* Transmits messages to the blockchain through the tx event chain
* @function send
* @alias module:lib/operations/messages.send
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function send(req, res, operationId, callback) {
const wfMessage = req.body;
if (wfMessage.MetaHeader && wfMessage.MetaHeader.autoGenerated) wfMessage.MetaHeader.autoGenerated = false;
wfTxEvent.emit('messageCommitted', wfMessage, function endpointSendCb(err, wfMessage) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
// Send response using common endpoint response function
return response.sendImperative(res, err, resBody, wfMessage, callback);
});
}
/**
* Receives messages by triggering the rx event chain
* @function receive
* @alias module:lib/operations/messages.receive
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function receive(req, res, operationId, callback) {
const wfMessage = req.body;
wfRxEvent.emit('messageReceived', wfMessage, function endpointReceiveCb(err, wfMessage) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
// Send response using common endpoint response function
return response.sendImperative(res, err, resBody, wfMessage, callback);
});
}
/**
* Checks whether a Whiteflag message is valid
* @function validate
* @alias module:lib/operations/messages.validate
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function validate(req, res, operationId, callback) {
const wfMessage = req.body;
wfCodec.verifyFormat(wfMessage, function endpointVerifyFormatCb(err, wfMessage) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
// Return response if format is not valid
if (err) return response.sendIndicative(res, err, resBody, wfMessage, callback);
// Add info message if format is valid
if (wfMessage.MetaHeader.formatValid) {
resBody.meta.info = array.addItem(resBody.meta.info, 'Message format is valid');
}
// Verify message reference
wfReference.verify(wfMessage, function endpointVerifyReferenceCb(err, wfMessage) {
if (!err && wfMessage.MetaHeader.referenceValid) {
resBody.meta.info = array.addItem(resBody.meta.info, 'Message reference is valid');
}
return response.sendIndicative(res, err, resBody, wfMessage, callback);
});
});
}
/**
* Encodes a Whiteflag message
* @function encode
* @alias module:lib/operations/messages.encode
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function encode(req, res, operationId, callback) {
const wfMessage = req.body;
wfCodec.encode(wfMessage, function endpointEncodeCb(err, wfMessage) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
// Send response using common endpoint response function
return response.sendImperative(res, err, resBody, wfMessage, callback);
});
}
/**
* Decodes a Whiteflag message
* @function decode
* @alias module:lib/operations/messages.decode
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function decode(req, res, operationId, callback) {
const wfMessage = req.body;
wfCodec.decode(wfMessage, function endpointDecodeCb(err, wfMessage, ivMissing) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
// Check for missing decryption key
if (err && err.code === 'WF_ENCRYPTION_ERROR'
&& !wfMessage.MetaHeader.encryptionKeyInput
) {
err = new ProcessingError('Could not decrypt message', [ 'Encryption key input is missing', err.message ], 'WF_API_BAD_REQUEST');
}
// Check for missing intitialisation vector
if (!err && ivMissing) {
err = new ProcessingError('Could not decrypt message', [ 'Initialisation vector is missing' ], 'WF_API_BAD_REQUEST');
}
// Send response using common endpoint response function
return response.sendImperative(res, err, resBody, wfMessage, callback);
});
}
/**
* Retrieves all messages from the database
* @function getMessages
* @alias module:lib/operations/messages.getMessages
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function getMessages(req, res, operationId, callback) {
let wfQuery = {};
let resBody = response.createBody(req, operationId);
resBody.meta.query = req.query;
let parameters = Object.keys(req.query);
if (parameters.length !== 0) {
parameters.forEach(parameter => {
switch (parameter) {
// Integers
case 'blockNumber':
case 'blockDepth': {
wfQuery[`MetaHeader.${parameter}`] = parseInt(req.query[parameter]);
break;
}
// Booleans
case 'autoGenerated':
case 'transmissionSuccess':
case 'confirmed':
case 'originatorValid':
case 'referenceValid':
case 'formatValid': {
wfQuery[`MetaHeader.${parameter}`] = (req.query[parameter] === 'true');
break;
}
// Strings
default: {
wfQuery[`MetaHeader.${parameter}`] = req.query[parameter];
}
}
});
}
wfRetrieve.getQuery(wfQuery, function endpointGetMessagesDbCb(err, wfMessages) {
return processMessages(err, res, resBody, wfMessages, callback);
});
}
/**
* Retrieves message references from the database
* @function getReferences
* @alias module:lib/operations/messages.getReferences
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function getReferences(req, res, operationId, callback) {
const query = req.query || {};
let resBody = response.createBody(req, operationId);
resBody.meta.query = query;
// Retrieve references if transaction hash is specified as query parameter
if (query.transactionHash && query.blockchain) {
return wfRetrieve.getReferences(query.transactionHash, query.blockchain,
function endpointGetReferencesBcCb(err, wfMessages) {
processMessages(err, res, resBody, wfMessages, callback);
}
);
}
if (query.transactionHash) {
return wfRetrieve.getReferences(query.transactionHash, null,
function endpointGetReferencesCb(err, wfMessages) {
processMessages(err, res, resBody, wfMessages, callback);
}
);
}
// Cannot retrieve message references without transaction hash
let err = new ProcessingError('Query does not contain transactionHash of referencing message', null, 'WF_API_BAD_REQUEST');
return processMessages(err, res, resBody, null, callback);
}
/**
* Retrieves message sequences from the database
* @function getSequence
* @alias module:lib/operations/messages.getSequence
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function getSequence(req, res, operationId, callback) {
const query = req.query || {};
let resBody = response.createBody(req, operationId);
resBody.meta.query = query;
// Retrieve sequence if transaction hash is specified as query parameter
if (query.transactionHash && query.blockchain) {
return wfRetrieve.getSequence(query.transactionHash, query.blockchain,
function endpointGetSequenceBcCb(err, wfMessages) {
processMessages(err, res, resBody, wfMessages, callback);
}
);
}
if (query.transactionHash) {
return wfRetrieve.getSequence(query.transactionHash, null,
function endpointGetSequenceCb(err, wfMessages) {
processMessages(err, res, resBody, wfMessages, callback);
}
);
}
// Cannot retrieve message sequence without trabsaction hash
let err = new ProcessingError('Query does not contain transactionHash of first message in sequence', null, 'WF_API_BAD_REQUEST');
return processMessages(err, res, resBody, null, callback);
}
// PRIVATE MODULE FUNCTIONS //
/**
* Returns messages from queries on resource endpoints
* @private
* @param {Object} req the http request
* @param {Object} res the http response
* @param {Object} resBody the response body
* @param {Array} wfMessages Whiteflag messages
* @param {logEndpointEventCb} callback
*/
function processMessages(err, res, resBody, wfMessages, callback) {
// Ensure message are in an array
let resData = [];
if (!err) {
if (Array.isArray(wfMessages)) resData = wfMessages;
else resData = [ wfMessages ];
}
// Send response using common endpoint response function
return response.sendIndicative(res, err, resBody, resData, callback);
}