flow: default service, gateway, emitters
This commit is contained in:
parent
dc9ffcbd0e
commit
028518ef1e
|
@ -1,3 +1,5 @@
|
||||||
npm_config_registry=https://npm.i12e.cha0s.io
|
npm_config_registry=https://npm.i12e.cha0s.io
|
||||||
|
|
||||||
NODE_PATH=/var/node/dist/node_modules
|
NODE_PATH=/var/node/dist/node_modules
|
||||||
|
|
||||||
|
GATEWAY_EMITTERS=@truss/emitters/http
|
||||||
|
|
1
services/default/.gitignore
vendored
Normal file
1
services/default/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
2
services/default/actions.js
Normal file
2
services/default/actions.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module.exports = (serviceMap) => ({
|
||||||
|
});
|
3
services/default/compose.js
Normal file
3
services/default/compose.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = (config) => {
|
||||||
|
};
|
||||||
|
|
2
services/default/hooks.js
Normal file
2
services/default/hooks.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module.exports = () => ({
|
||||||
|
});
|
15
services/default/index.js
Normal file
15
services/default/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const {createDispatcher} = require('@truss/comm');
|
||||||
|
|
||||||
|
const dispatcher = createDispatcher();
|
||||||
|
dispatcher.lookupActions(require('./actions'));
|
||||||
|
dispatcher.lookupHooks(require('./hooks'));
|
||||||
|
dispatcher.connect();
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.accept('./actions', () => {
|
||||||
|
dispatcher.lookupActions(require('./actions'));
|
||||||
|
});
|
||||||
|
module.hot.accept('./hooks', () => {
|
||||||
|
dispatcher.lookupHooks(require('./hooks'));
|
||||||
|
});
|
||||||
|
}
|
17
services/default/package.json
Normal file
17
services/default/package.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Default service. Clone or customize!",
|
||||||
|
"main": "index.js",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "node -e '' -r '@truss/webpack/task/build'",
|
||||||
|
"default": "yarn run dev",
|
||||||
|
"dev": "node -e '' -r '@truss/webpack/task/scaffold'"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@truss/comm": "1.x",
|
||||||
|
"@truss/webpack": "1.x"
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,18 +2,18 @@ const {sendActionToService} = require('@truss/comm');
|
||||||
|
|
||||||
const debug = require('debug')('truss:gateway:actions');
|
const debug = require('debug')('truss:gateway:actions');
|
||||||
|
|
||||||
module.exports = (serviceMap) => ({
|
module.exports = (hooks) => ({
|
||||||
'truss/hook-services': ({payload: {hook, args}}) => {
|
'@truss/hook-services': ({payload: {hook, args}}) => {
|
||||||
if (!(hook in serviceMap.hooks)) { return []; }
|
if (!(hook in hooks)) { return []; }
|
||||||
return serviceMap.hooks[hook];
|
return hooks[hook];
|
||||||
},
|
},
|
||||||
'truss/invoke': ({payload: {hook, args}}) => {
|
'@truss/invoke': ({payload: {hook, args}}) => {
|
||||||
if (!(hook in serviceMap.hooks)) { return {}; }
|
if (!(hook in hooks)) { return {}; }
|
||||||
return invokeHook(serviceMap.hooks[hook], hook, args);
|
return invokeHook(hooks[hook], hook, args);
|
||||||
},
|
},
|
||||||
'truss/invoke-flat': ({payload: {hook, args}}) => {
|
'@truss/invoke-flat': ({payload: {hook, args}}) => {
|
||||||
if (!(hook in serviceMap.hooks)) { return []; }
|
if (!(hook in hooks)) { return []; }
|
||||||
return invokeHookFlat(serviceMap.hooks[hook], hook, args);
|
return invokeHookFlat(hooks[hook], hook, args);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ function invokeHookFlat(services, hook, args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function invokeHookFlatInternal(services, hook, args) {
|
function invokeHookFlatInternal(services, hook, args) {
|
||||||
const action = {type: 'truss/hook', payload: {hook, args}};
|
const action = {type: '@truss/hook', payload: {hook, args}};
|
||||||
return Promise.all(services.map((service) => {
|
return Promise.all(services.map((service) => {
|
||||||
return sendActionToService(action, service);
|
return sendActionToService(action, service);
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -2,4 +2,3 @@ module.exports = (config) => {
|
||||||
const gatewayPort = process.env.GATEWAY_PORT || 8000;
|
const gatewayPort = process.env.GATEWAY_PORT || 8000;
|
||||||
config.ports = [`${gatewayPort}:8000`];
|
config.ports = [`${gatewayPort}:8000`];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,82 +1,40 @@
|
||||||
|
// Core.
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
// 3rd party.
|
||||||
const debug = require('debug')('truss:gateway');
|
const debug = require('debug')('truss:gateway');
|
||||||
|
// 2nd party.
|
||||||
const {createDispatcher, sendActionToService} = require('@truss/comm');
|
const {createDispatcher} = require('@truss/comm');
|
||||||
|
// 1st party.
|
||||||
let listener = require('./listener').default;
|
const {loadServiceMap} = require('./service-map');
|
||||||
|
// Main.
|
||||||
// ---
|
(async () => {
|
||||||
|
try {
|
||||||
if (!process.env.SERVICES) {
|
debug(`loading service map...`);
|
||||||
console.error('$SERVICES must be defined!');
|
const serviceMap = await loadServiceMap();
|
||||||
process.exit(1);
|
const {executors, hooks, listeners} = serviceMap;
|
||||||
}
|
|
||||||
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((serviceMap, schema, i) => {
|
|
||||||
const service = services[i];
|
|
||||||
for (const type of (schema.executors || [])) {
|
|
||||||
if (serviceMap.executors[type]) {
|
|
||||||
throw new RangeError(`
|
|
||||||
Only one executor may be specified per action type! "${service}"
|
|
||||||
tried to register an executor but "${serviceMap.executors[type]}"
|
|
||||||
already registered one for "${type}".
|
|
||||||
`.replace(/\s+/g, ' ').trim());
|
|
||||||
}
|
|
||||||
serviceMap.executors[type] = service;
|
|
||||||
}
|
|
||||||
for (const type of (schema.listeners || [])) {
|
|
||||||
serviceMap.listeners[type] = serviceMap.listeners[type] || [];
|
|
||||||
serviceMap.listeners[type].push(service);
|
|
||||||
}
|
|
||||||
for (const hook of (schema.hooks || [])) {
|
|
||||||
serviceMap.hooks[hook] = serviceMap.hooks[hook] || [];
|
|
||||||
serviceMap.hooks[hook].push(service);
|
|
||||||
}
|
|
||||||
return serviceMap;
|
|
||||||
}, {
|
|
||||||
executors: {},
|
|
||||||
hooks: {},
|
|
||||||
listeners: {},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// hey, listen
|
|
||||||
const httpServer = http.createServer();
|
|
||||||
reduceServiceMap$.then((serviceMap) => {
|
|
||||||
debug(`service map: ${JSON.stringify(serviceMap, null, ' ')}`);
|
debug(`service map: ${JSON.stringify(serviceMap, null, ' ')}`);
|
||||||
|
|
||||||
const dispatcher = createDispatcher();
|
const dispatcher = createDispatcher();
|
||||||
dispatcher.setArgs(serviceMap);
|
dispatcher.setArgs(hooks);
|
||||||
dispatcher.lookupActions(require('./actions'));
|
dispatcher.lookupActions(require('./actions'));
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
module.hot.accept('./actions', () => {
|
module.hot.accept('./actions', () => {
|
||||||
dispatcher.lookupActions(require('./actions'));
|
dispatcher.lookupActions(require('./actions'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const emitterPaths = process.env.GATEWAY_EMITTERS.split(':');
|
||||||
|
const emitters = [];
|
||||||
|
for (const emitterPath of emitterPaths) {
|
||||||
|
const Class = __non_webpack_require__(emitterPath);
|
||||||
|
emitters.push(new Class(executors, listeners));
|
||||||
|
}
|
||||||
const server = dispatcher.connect();
|
const server = dispatcher.connect();
|
||||||
server.on('listening', () => {
|
server.on('listening', () => {
|
||||||
const port = process.env.GATEWAY_PORT || 8000;
|
for (const emitter of emitters) {
|
||||||
httpServer.listen(port);
|
emitter.listen();
|
||||||
httpServer.on('listening', () => {
|
}
|
||||||
debug(`HTTP listening on port ${port}...`);
|
|
||||||
})
|
|
||||||
httpServer.on('request', (req, res) => {
|
|
||||||
listener(serviceMap, req, res);
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}).catch(console.error);
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
if (module.hot) {
|
}
|
||||||
module.hot.accept('./listener', () => {
|
})();
|
||||||
listener = require('./listener').default;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,25 +7,32 @@ const debug = require('debug')('truss:gateway:listener');
|
||||||
const parser = bodyParser.json();
|
const parser = bodyParser.json();
|
||||||
|
|
||||||
export default function(serviceMap, req, res) { parser(req, res, () => {
|
export default function(serviceMap, req, res) { parser(req, res, () => {
|
||||||
debug(`HTTP ${req.method} ${req.url}`);
|
const method = req.method.toLowerCase();
|
||||||
|
debug(`HTTP ${method} ${req.url}`);
|
||||||
// map to action
|
// map to action
|
||||||
const action = {type: undefined, payload: {url: req.url}};
|
const action = {
|
||||||
switch (req.method) {
|
type: undefined,
|
||||||
case 'POST':
|
payload: {
|
||||||
|
headers: req.headers,
|
||||||
|
method: method,
|
||||||
|
url: req.url,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch (method) {
|
||||||
|
case 'post':
|
||||||
// truss action
|
// truss action
|
||||||
if (req.url.startsWith('/truss/action/')) {
|
if (req.url.startsWith('/truss/action/')) {
|
||||||
action.type = req.url.substr('/truss/action/'.length);
|
action.type = req.url.substr('/truss/action/'.length);
|
||||||
}
|
}
|
||||||
// truss/http-post
|
// @truss/http-post
|
||||||
else {
|
else {
|
||||||
action.type = 'truss/http-post';
|
action.type = '@truss/http-post';
|
||||||
}
|
}
|
||||||
action.payload.body = req.body;
|
action.payload.body = req.body;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// truss/http-(get|delete|...)
|
// @truss/http-(get|delete|...)
|
||||||
action.type = `truss/http-${req.method.toLowerCase()}`;
|
action.type = `@truss/http-${method}`;
|
||||||
action.payload.headers = req.headers;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// listeners...
|
// listeners...
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@truss/comm": "1.x",
|
"@truss/comm": "1.x",
|
||||||
|
"@truss/emitters": "1.x",
|
||||||
"@truss/webpack": "1.x",
|
"@truss/webpack": "1.x",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
"debug": "3.1.0"
|
"debug": "3.1.0"
|
||||||
|
|
43
services/gateway/service-map.js
Normal file
43
services/gateway/service-map.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
const {sendActionToService} = require('@truss/comm');
|
||||||
|
|
||||||
|
if (!process.env.SERVICES) {
|
||||||
|
console.error('$SERVICES must be defined!');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const services = process.env.SERVICES.split(',');
|
||||||
|
|
||||||
|
export function loadServiceMap() {
|
||||||
|
// get all schemas
|
||||||
|
const schemas$ = services.map((service) => {
|
||||||
|
return sendActionToService({type: '@truss/schema'}, service);
|
||||||
|
});
|
||||||
|
// build service map
|
||||||
|
return Promise.all(schemas$).then((schemas) => {
|
||||||
|
return schemas.reduce((serviceMap, schema, i) => {
|
||||||
|
const service = services[i];
|
||||||
|
for (const type of (schema.executors || [])) {
|
||||||
|
if (serviceMap.executors[type]) {
|
||||||
|
throw new RangeError(`
|
||||||
|
Only one executor may be specified per action type! "${service}"
|
||||||
|
tried to register an executor but "${serviceMap.executors[type]}"
|
||||||
|
already registered one for "${type}".
|
||||||
|
`.replace(/\s+/g, ' ').trim());
|
||||||
|
}
|
||||||
|
serviceMap.executors[type] = service;
|
||||||
|
}
|
||||||
|
for (const type of (schema.listeners || [])) {
|
||||||
|
serviceMap.listeners[type] = serviceMap.listeners[type] || [];
|
||||||
|
serviceMap.listeners[type].push(service);
|
||||||
|
}
|
||||||
|
for (const hook of (schema.hooks || [])) {
|
||||||
|
serviceMap.hooks[hook] = serviceMap.hooks[hook] || [];
|
||||||
|
serviceMap.hooks[hook].push(service);
|
||||||
|
}
|
||||||
|
return serviceMap;
|
||||||
|
}, {
|
||||||
|
executors: {},
|
||||||
|
hooks: {},
|
||||||
|
listeners: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user