From 028518ef1e00ac3761c8d1a642f7fea7ba67a758 Mon Sep 17 00:00:00 2001 From: cha0s Date: Tue, 5 Mar 2019 23:08:28 -0600 Subject: [PATCH] flow: default service, gateway, emitters --- .common.env | 2 + services/default/.gitignore | 1 + services/default/actions.js | 2 + services/default/compose.js | 3 + services/default/hooks.js | 2 + services/default/index.js | 15 +++++ services/default/package.json | 17 +++++ services/gateway/actions.js | 22 +++---- services/gateway/compose.js | 1 - services/gateway/index.js | 112 ++++++++++---------------------- services/gateway/listener.js | 25 ++++--- services/gateway/package.json | 1 + services/gateway/service-map.js | 43 ++++++++++++ 13 files changed, 148 insertions(+), 98 deletions(-) create mode 100644 services/default/.gitignore create mode 100644 services/default/actions.js create mode 100644 services/default/compose.js create mode 100644 services/default/hooks.js create mode 100644 services/default/index.js create mode 100644 services/default/package.json create mode 100644 services/gateway/service-map.js diff --git a/.common.env b/.common.env index 12e0421..e9caac2 100644 --- a/.common.env +++ b/.common.env @@ -1,3 +1,5 @@ npm_config_registry=https://npm.i12e.cha0s.io NODE_PATH=/var/node/dist/node_modules + +GATEWAY_EMITTERS=@truss/emitters/http diff --git a/services/default/.gitignore b/services/default/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/services/default/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/services/default/actions.js b/services/default/actions.js new file mode 100644 index 0000000..139b30d --- /dev/null +++ b/services/default/actions.js @@ -0,0 +1,2 @@ +module.exports = (serviceMap) => ({ +}); diff --git a/services/default/compose.js b/services/default/compose.js new file mode 100644 index 0000000..83f7ebe --- /dev/null +++ b/services/default/compose.js @@ -0,0 +1,3 @@ +module.exports = (config) => { +}; + diff --git a/services/default/hooks.js b/services/default/hooks.js new file mode 100644 index 0000000..bd318b8 --- /dev/null +++ b/services/default/hooks.js @@ -0,0 +1,2 @@ +module.exports = () => ({ +}); diff --git a/services/default/index.js b/services/default/index.js new file mode 100644 index 0000000..31acfe2 --- /dev/null +++ b/services/default/index.js @@ -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')); + }); +} diff --git a/services/default/package.json b/services/default/package.json new file mode 100644 index 0000000..58d820a --- /dev/null +++ b/services/default/package.json @@ -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" + } +} diff --git a/services/gateway/actions.js b/services/gateway/actions.js index 9c49e92..b6db87d 100644 --- a/services/gateway/actions.js +++ b/services/gateway/actions.js @@ -2,18 +2,18 @@ const {sendActionToService} = require('@truss/comm'); const debug = require('debug')('truss:gateway:actions'); -module.exports = (serviceMap) => ({ - 'truss/hook-services': ({payload: {hook, args}}) => { - if (!(hook in serviceMap.hooks)) { return []; } - return serviceMap.hooks[hook]; +module.exports = (hooks) => ({ + '@truss/hook-services': ({payload: {hook, args}}) => { + if (!(hook in hooks)) { return []; } + return hooks[hook]; }, - 'truss/invoke': ({payload: {hook, args}}) => { - if (!(hook in serviceMap.hooks)) { return {}; } - return invokeHook(serviceMap.hooks[hook], hook, args); + '@truss/invoke': ({payload: {hook, args}}) => { + if (!(hook in hooks)) { return {}; } + return invokeHook(hooks[hook], hook, args); }, - 'truss/invoke-flat': ({payload: {hook, args}}) => { - if (!(hook in serviceMap.hooks)) { return []; } - return invokeHookFlat(serviceMap.hooks[hook], hook, args); + '@truss/invoke-flat': ({payload: {hook, args}}) => { + if (!(hook in hooks)) { return []; } + return invokeHookFlat(hooks[hook], hook, args); }, }); @@ -23,7 +23,7 @@ function invokeHookFlat(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 sendActionToService(action, service); })); diff --git a/services/gateway/compose.js b/services/gateway/compose.js index dd0eff6..ed3a9cb 100644 --- a/services/gateway/compose.js +++ b/services/gateway/compose.js @@ -2,4 +2,3 @@ module.exports = (config) => { const gatewayPort = process.env.GATEWAY_PORT || 8000; config.ports = [`${gatewayPort}:8000`]; }; - diff --git a/services/gateway/index.js b/services/gateway/index.js index cc79709..384e46d 100644 --- a/services/gateway/index.js +++ b/services/gateway/index.js @@ -1,82 +1,40 @@ +// Core. const http = require('http'); - +// 3rd party. const debug = require('debug')('truss:gateway'); - -const {createDispatcher, sendActionToService} = require('@truss/comm'); - -let listener = require('./listener').default; - -// --- - -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((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()); +// 2nd party. +const {createDispatcher} = require('@truss/comm'); +// 1st party. +const {loadServiceMap} = require('./service-map'); +// Main. +(async () => { + try { + debug(`loading service map...`); + const serviceMap = await loadServiceMap(); + const {executors, hooks, listeners} = serviceMap; + debug(`service map: ${JSON.stringify(serviceMap, null, ' ')}`); + const dispatcher = createDispatcher(); + dispatcher.setArgs(hooks); + dispatcher.lookupActions(require('./actions')); + if (module.hot) { + module.hot.accept('./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(); + server.on('listening', () => { + for (const emitter of emitters) { + emitter.listen(); } - 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, ' ')}`); - - const dispatcher = createDispatcher(); - dispatcher.setArgs(serviceMap); - dispatcher.lookupActions(require('./actions')); - if (module.hot) { - module.hot.accept('./actions', () => { - dispatcher.lookupActions(require('./actions')); }); } - const server = dispatcher.connect(); - server.on('listening', () => { - 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); - }); - }); -}).catch(console.error); - -if (module.hot) { - module.hot.accept('./listener', () => { - listener = require('./listener').default; - }); -} + catch (error) { + console.error(error); + } +})(); diff --git a/services/gateway/listener.js b/services/gateway/listener.js index 846a5ca..902a43b 100644 --- a/services/gateway/listener.js +++ b/services/gateway/listener.js @@ -7,25 +7,32 @@ const debug = require('debug')('truss:gateway:listener'); const parser = bodyParser.json(); 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 - const action = {type: undefined, payload: {url: req.url}}; - switch (req.method) { - case 'POST': + const action = { + type: undefined, + payload: { + headers: req.headers, + method: method, + url: req.url, + } + }; + switch (method) { + case 'post': // truss action if (req.url.startsWith('/truss/action/')) { action.type = req.url.substr('/truss/action/'.length); } - // truss/http-post + // @truss/http-post else { - action.type = 'truss/http-post'; + action.type = '@truss/http-post'; } action.payload.body = req.body; break; default: - // truss/http-(get|delete|...) - action.type = `truss/http-${req.method.toLowerCase()}`; - action.payload.headers = req.headers; + // @truss/http-(get|delete|...) + action.type = `@truss/http-${method}`; break; } // listeners... diff --git a/services/gateway/package.json b/services/gateway/package.json index 81cb2dc..15090ab 100644 --- a/services/gateway/package.json +++ b/services/gateway/package.json @@ -12,6 +12,7 @@ "license": "MIT", "dependencies": { "@truss/comm": "1.x", + "@truss/emitters": "1.x", "@truss/webpack": "1.x", "body-parser": "1.18.3", "debug": "3.1.0" diff --git a/services/gateway/service-map.js b/services/gateway/service-map.js new file mode 100644 index 0000000..737c5f7 --- /dev/null +++ b/services/gateway/service-map.js @@ -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: {}, + }); + }); +}