102 lines
2.8 KiB
JavaScript
102 lines
2.8 KiB
JavaScript
|
const http = require('http');
|
||
|
|
||
|
const debug = require('debug')('truss:gateway');
|
||
|
|
||
|
const {createDispatcher, sendActionToService} = require('@truss/truss');
|
||
|
|
||
|
let listener = require('./listener');
|
||
|
|
||
|
// ---
|
||
|
|
||
|
if (!process.env.SERVICES) {
|
||
|
console.error('$SERVICES must be defined!');
|
||
|
process.exit(1);
|
||
|
}
|
||
|
const services = process.env.SERVICES.split(',');
|
||
|
|
||
|
// get all schemas
|
||
|
const schemas$ = services.map((service) => {
|
||
|
return sendActionToService({type: 'truss/schema'}, service);
|
||
|
});
|
||
|
|
||
|
// build service map
|
||
|
const reduceServiceMap$ = Promise.all(schemas$).then((schemas) => {
|
||
|
return schemas.reduce((l, r, i) => {
|
||
|
for (const type of (r.executors || [])) {
|
||
|
if (l.executors[type]) {
|
||
|
throw new Error(`
|
||
|
Only one executor may be specified per action type! "${services[i]}" tried to
|
||
|
register an executor but "${l.executors[type]}" already registered one for
|
||
|
${type}.
|
||
|
`);
|
||
|
}
|
||
|
l.executors[type] = services[i];
|
||
|
}
|
||
|
for (const type of (r.listeners || [])) {
|
||
|
(l.listeners[type] = l.listeners[type] || []).push(services[i]);
|
||
|
}
|
||
|
for (const hook of (r.hooks || [])) {
|
||
|
(l.hooks[hook] = l.hooks[hook] || []).push(services[i]);
|
||
|
}
|
||
|
return l;
|
||
|
}, {
|
||
|
executors: {},
|
||
|
hooks: {},
|
||
|
listeners: {},
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// hey, listen
|
||
|
const httpServer = http.createServer();
|
||
|
reduceServiceMap$.then((serviceMap) => {
|
||
|
debug(`service map: ${JSON.stringify(serviceMap)}`);
|
||
|
|
||
|
const port = process.env.GATEWAY_PORT || 8000;
|
||
|
httpServer.listen(port);
|
||
|
httpServer.on('listening', () => {
|
||
|
debug(`HTTP listening on port ${port}...`);
|
||
|
})
|
||
|
httpServer.on('request', (req, res) => {
|
||
|
listener(serviceMap, req, res);
|
||
|
});
|
||
|
|
||
|
// hooks
|
||
|
createDispatcher({
|
||
|
'truss/hook-services': ({payload: {hook, args}}) => {
|
||
|
if (!(hook in serviceMap.hooks)) { return []; }
|
||
|
return serviceMap.hooks[hook];
|
||
|
},
|
||
|
'truss/invoke': ({payload: {hook, args}}) => {
|
||
|
if (!(hook in serviceMap.hooks)) { return {}; }
|
||
|
return invokeHook(serviceMap.hooks[hook], hook, args);
|
||
|
},
|
||
|
'truss/invoke-flat': ({payload: {hook, args}}) => {
|
||
|
if (!(hook in serviceMap.hooks)) { return []; }
|
||
|
return invokeHookFlat(serviceMap.hooks[hook], hook, args);
|
||
|
},
|
||
|
}).connect();
|
||
|
}).catch(console.error);
|
||
|
|
||
|
function invokeHookFlat(services, hook, args) {
|
||
|
debug(`invoking hook flat(${hook}(${JSON.stringify(args)}))...`);
|
||
|
|
||
|
const action = {type: 'truss/hook', payload: {hook, args}};
|
||
|
return Promise.all(services.map((service) => {
|
||
|
return sendActionToService(action, service);
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
function invokeHook(services, hook, args) {
|
||
|
debug(`invoking hook ${hook}(${JSON.stringify(args)})...`);
|
||
|
|
||
|
return invokeHookFlat(services, hook, args).then((result) => {
|
||
|
return result.reduce((l, r, i) => (l[services[i]] = r, l), {});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (module.hot) {
|
||
|
module.hot.accept('./listener', () => {
|
||
|
listener = require('./listener');
|
||
|
});
|
||
|
}
|