orator
Version:
Unopinionated API http server abstraction - REST or IPC
340 lines (297 loc) • 10.4 kB
JavaScript
const libOratorServiceServerBase = require('orator-serviceserver-base');
// A synthesized response object, for simple IPC.
const libOratorServiceServerIPCSynthesizedResponse = require('./Orator-ServiceServer-IPC-SynthesizedResponse.js');
// This library is the default router for our services
const libFindMyWay = require('find-my-way');
/**
* OratorServiceServerIPC class.
*
* @class
* @extends libOratorServiceServerBase
* @classdesc Represents an IPC service server for Orator.
*/
class OratorServiceServerIPC extends libOratorServiceServerBase
{
constructor(pFable, pOptions, pServiceHash)
{
super(pFable, pOptions, pServiceHash);
this.router = libFindMyWay(this.options);
this.ServiceServerType = 'IPC';
this.URL = 'IPC';
this.Port = 0;
this.preBehaviorFunctions = [];
this.behaviorMap = {};
this.postBehaviorFunctions = [];
}
use(fHandlerFunction)
{
return this.addPreBehaviorFunction(fHandlerFunction);
}
addPreBehaviorFunction(fHandlerFunction)
{
if (!super.use(fHandlerFunction))
{
this.log.error(`IPC provider failed to map USE handler function!`);
return false;
}
this.preBehaviorFunctions.push(fHandlerFunction);
return true;
}
executePreBehaviorFunctions(pRequest, pResponse, fNext)
{
let tmpAnticipate = this.fable.serviceManager.instantiateServiceProviderWithoutRegistration('Anticipate');
for (let i = 0; i < this.preBehaviorFunctions.length; i++)
{
let tmpPreBehaviorFunction = this.preBehaviorFunctions[i];
tmpAnticipate.anticipate(
(fStageComplete) =>
{
return tmpPreBehaviorFunction(pRequest, pResponse, fStageComplete);
});
}
tmpAnticipate.wait(
(pError) =>
{
if (pError)
{
this.log.error(`IPC Provider preBehaviorFunction failed with error: ${pError}`, pError);
}
return fNext(pError);
});
}
addPostBehaviorFunction(fHandlerFunction)
{
if (!super.use(fHandlerFunction))
{
this.log.error(`IPC provider failed to map USE handler function!`);
return false;
}
this.postBehaviorFunctions.push(fHandlerFunction);
return true;
}
executePostBehaviorFunctions(pRequest, pResponse, fNext)
{
let tmpAnticipate = this.fable.serviceManager.instantiateServiceProviderWithoutRegistration('Anticipate');
for (let i = 0; i < this.postBehaviorFunctions.length; i++)
{
let tmpPostBehaviorFunction = this.postBehaviorFunctions[i];
tmpAnticipate.anticipate(
(fStageComplete) =>
{
return tmpPostBehaviorFunction(pRequest, pResponse, fStageComplete);
});
}
tmpAnticipate.wait(
(pError) =>
{
if (pError)
{
this.log.error(`IPC Provider postBehaviorFunction failed with error: ${pError}`, pError);
}
return fNext(pError);
});
}
/*
* Service Route Creation Functions
*
* These base functions provide basic validation for the routes, but don't actually
* do anything with them. The design intent here is to allow derived classes to call
* these functions to validate that they conform to expected standards.
*
* Something like:
get (pRoute, ...fRouteProcessingFunctions)
{
//...now we can do our actual get mapping function!....
}
* This pattern and calling super is totally optional, obviously.
*************************************************************************/
addRouteProcessor(pMethod, pRoute, pRouteFunctionArray)
{
// We have a constrainer on IPC so we can control channels eventually, if we like.
// For now it just makes sure it was added with an IPC service server.
this.router.on(pMethod, pRoute, this.buildFindMyWayHandler(pRouteFunctionArray));
return true;
}
buildFindMyWayHandler(pRouteFunctionArray)
{
let tmpRouteFunctionArray = pRouteFunctionArray;
return (
(pRequest, pResponse, pData) =>
{
let tmpAnticipate = this.fable.serviceManager.instantiateServiceProviderWithoutRegistration('Anticipate');
tmpAnticipate.anticipate(
(fNext)=>
{
return this.executePreBehaviorFunctions(pRequest, pResponse, fNext);
});
for (let i = 0; i < tmpRouteFunctionArray.length; i++)
{
let tmpRouteFunction = tmpRouteFunctionArray[i];
tmpAnticipate.anticipate(
(fNext) =>
{
return tmpRouteFunction(pRequest, pResponse, fNext);
});
}
tmpAnticipate.anticipate(
(fStageComplete)=>
{
return this.executePostBehaviorFunctions(pRequest, pResponse, fStageComplete);
});
return new Promise(
(fResolve, fReject) =>
{
tmpAnticipate.wait(
(pBehaviorFunctionError) =>
{
if (pBehaviorFunctionError)
{
this.log.error(`IPC Provider behavior function failed with error: ${pBehaviorFunctionError}`, pBehaviorFunctionError);
return fReject(pBehaviorFunctionError);
}
return fResolve();
});
});
});
}
// This is the virtualized "body parser"
bodyParser()
{
return (pRequest, pResponse, fNext) =>
{
return fNext();
};
}
/**
* Handles the HTTP GET request for a specific route.
*
* @param {string} pRoute - The route to handle.
* @param {...Function} fRouteProcessingFunctions - The processing functions to execute for the route.
* @returns {Promise} A promise that resolves when the route processing is complete.
*/
doGet(pRoute, ...fRouteProcessingFunctions)
{
return this.addRouteProcessor('GET', pRoute, Array.from(fRouteProcessingFunctions));
}
/**
* Handles the PUT request for a specific route.
*
* @param {string} pRoute - The route to handle.
* @param {...Function} fRouteProcessingFunctions - The processing functions to execute for the route.
* @returns {Promise} - A promise that resolves when the route processing is complete.
*/
doPut(pRoute, ...fRouteProcessingFunctions)
{
return this.addRouteProcessor('PUT', pRoute, Array.from(fRouteProcessingFunctions));
}
/**
* Handles the HTTP POST request for a specific route.
*
* @param {string} pRoute - The route to handle.
* @param {...Function} fRouteProcessingFunctions - The processing functions to execute for the route.
* @returns {Promise} - A promise that resolves when the route processing is complete.
*/
doPost(pRoute, ...fRouteProcessingFunctions)
{
return this.addRouteProcessor('POST', pRoute, Array.from(fRouteProcessingFunctions));
}
/**
* Handles the HTTP DEL request for a specific route.
*
* @param {string} pRoute - The route to be deleted.
* @param {...Function} fRouteProcessingFunctions - The route processing functions to be added.
* @returns {Object} - The updated route processor object.
*/
doDel(pRoute, ...fRouteProcessingFunctions)
{
return this.addRouteProcessor('DELETE', pRoute, Array.from(fRouteProcessingFunctions));
}
/**
* Adds a PATCH route processor to the service server.
*
* @param {string} pRoute - The route to be processed.
* @param {...Function} fRouteProcessingFunctions - The route processing functions.
* @returns {boolean} - Returns true if the route processor was added successfully, false otherwise.
*/
doPatch(pRoute, ...fRouteProcessingFunctions)
{
return this.addRouteProcessor('PATCH', pRoute, Array.from(fRouteProcessingFunctions));
}
/**
* Adds a route processor for the OPTIONS method.
*
* @param {string} pRoute - The route to add the processor to.
* @param {...Function} fRouteProcessingFunctions - The processing functions to be executed for the route.
* @returns {Object} - The updated Orator-ServiceServer-IPC object.
*/
doOpts(pRoute, ...fRouteProcessingFunctions)
{
return this.addRouteProcessor('OPTIONS', pRoute, Array.from(fRouteProcessingFunctions));
}
/**
* Handles the HEAD request for a specific route.
*
* @param {string} pRoute - The route to handle.
* @param {...Function} fRouteProcessingFunctions - The processing functions to execute for the route.
* @returns {Promise} - A promise that resolves when the route processing is complete.
*/
doHead(pRoute, ...fRouteProcessingFunctions)
{
return this.addRouteProcessor('HEAD', pRoute, Array.from(fRouteProcessingFunctions));
}
/*************************************************************************
* End of Service Route Creation Functions
*/
/**
* Invokes a method on the IPC provider.
*
* @param {string} pMethod - The method to invoke.
* @param {string} pRoute - The route to invoke.
* @param {any} pData - The data to pass to the method.
* @param {Function} fCallback - The callback function to handle the response.
* @throws {Error} Throws an error if invoked without a callback function.
*/
invoke(pMethod, pRoute, pData, fCallback)
{
// If the data is skipped and a callback is parameter 3, do the right thing
let tmpCallback = (typeof(fCallback) == 'function') ? fCallback :
(typeof(pData) == 'function') ? pData :
false;
if (!tmpCallback)
{
throw new Error(`IPC Provider invoke() called without a callback function.`);
}
// Create a bare minimum request object for IPC to pass to our router
let tmpRequest = (
{
method: pMethod,
url: pRoute,
guid: this.fable.getUUID()
});
// For now, dealing with no handler constraints.
let tmpHandler = this.router.find( tmpRequest.method, tmpRequest.url);
// Create a container for the IPC response data to be aggregated to from send() methodds
let tmpSynthesizedResponseData = new libOratorServiceServerIPCSynthesizedResponse(tmpHandler, this.log, tmpRequest.guid);
// Map parsed params back to the request object
tmpRequest.params = tmpSynthesizedResponseData.params;
tmpRequest.searchParams = tmpSynthesizedResponseData.searchParams;
//params: handle._createParamsObject(params)//,
//searchParams: this.querystringParser(querystring)
tmpHandler.handler(tmpRequest, tmpSynthesizedResponseData, pData).then(
(pResults)=>
{
return tmpCallback(null, tmpSynthesizedResponseData.responseData, tmpSynthesizedResponseData, pResults);
},
(pError)=>
{
this.log.trace('IPC Response Received', {Error: pError});
if (pError)
{
this.log.error(`IPC Request Error Request GUID [${tmpRequest.guid}] handling route [${pRoute}]: ${pError}`, {Error: pError, Route: pRoute, Data: pData});
}
return tmpCallback(pError, tmpSynthesizedResponseData.responseData, tmpSynthesizedResponseData);
}
);
}
}
module.exports = OratorServiceServerIPC;