├── .babelrc ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── error.js ├── index.js └── proxy │ ├── commands │ └── FooCommand.js │ ├── handlers │ └── FooHandler.js │ └── index.js ├── lib ├── Command.js ├── CommandBus.js ├── CommandInterface.js ├── CreateCommandBusProxy.js ├── Middleware.js ├── exceptions │ ├── InvalidCommand.js │ ├── InvalidCommandException.js │ ├── InvalidHandlerMethod.js │ ├── InvalidHandlerMethodException.js │ ├── InvalidMiddleware.js │ ├── InvalidMiddlewareException.js │ ├── MissingHandler.js │ ├── MissingHandlerException.js │ └── createException.js ├── handler │ ├── CommandHandlerMiddleware.js │ ├── CommandNameExtractor │ │ ├── ClassNameExtractor.js │ │ └── CommandNameExtractor.js │ ├── Locator │ │ ├── HandlerLocator.js │ │ ├── InMemoryLocator.js │ │ └── NamespaceHandlerLocator.js │ └── MethodNameInflector │ │ ├── HandleInflector.js │ │ └── MethodNameInflector.js ├── index.js ├── plugins │ └── LoggerMiddleware.js └── utils.js ├── package.json ├── src ├── Command.js ├── CommandBus.js ├── CreateCommandBusProxy.js ├── Middleware.js ├── exceptions │ ├── InvalidCommandException.js │ ├── InvalidHandlerMethodException.js │ ├── InvalidMiddlewareException.js │ ├── MissingHandlerException.js │ └── createException.js ├── handler │ ├── CommandHandlerMiddleware.js │ ├── CommandNameExtractor │ │ ├── ClassNameExtractor.js │ │ └── CommandNameExtractor.js │ ├── Locator │ │ ├── HandlerLocator.js │ │ ├── InMemoryLocator.js │ │ └── NamespaceHandlerLocator.js │ └── MethodNameInflector │ │ ├── HandleClassNameInflector.js │ │ ├── HandleInflector.js │ │ └── MethodNameInflector.js ├── index.js ├── plugins │ └── LoggerMiddleware.js └── utils.js ├── test ├── CommandBus.js ├── CreateCommandBusProxy.js ├── Middleware.js ├── handler │ ├── CommandHandlerMiddleware.js │ ├── CommandNameExtractor │ │ ├── ClassNameExtractor.js │ │ └── CommandNameExtractor.js │ ├── Locator │ │ ├── HandlerLocator.js │ │ ├── InMemoryLocator.js │ │ └── NamespaceHandlerLocator.js │ └── MethodNameInflector │ │ ├── HandleInflector.js │ │ └── MethodNameInflector.js └── plugins │ └── LoggerMiddleware.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-flow", 4 | "@babel/preset-env" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib/**/*.js 2 | examples/**/*.js 3 | node_modules/**/*.js 4 | test/**/*.js 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-standard"], 3 | "rules": { 4 | "indent": [2, "tab"], 5 | "no-tabs": 0, 6 | "class-methods-use-this": 0, 7 | "space-before-function-paren": ["error", "never"], 8 | "no-underscore-dangle": 0, 9 | "no-console": 0 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /coverage/.* 3 | /dist/.* 4 | /node_modules/.* 5 | 6 | [include] 7 | 8 | [libs] 9 | 10 | [lints] 11 | 12 | [options] 13 | 14 | [strict] 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | *.lcov 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules/ 33 | jspm_packages/ 34 | 35 | # Typescript v1 declaration files 36 | typings/ 37 | 38 | # Optional npm cache directory 39 | .npm 40 | 41 | # Optional eslint cache 42 | .eslintcache 43 | 44 | # Optional REPL history 45 | .node_repl_history 46 | 47 | # Output of 'npm pack' 48 | *.tgz 49 | 50 | # Yarn Integrity file 51 | .yarn-integrity 52 | 53 | .vscode 54 | .DS_Store 55 | 56 | # Distribution build 57 | dist 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | 5 | cache: 6 | yarn: true 7 | directories: 8 | - "node_modules" 9 | 10 | script: 11 | - yarn test 12 | - yarn report:coverage 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Erick Torres 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Command Bus 2 | [![npm version](https://badge.fury.io/js/simple-command-bus.svg)](https://badge.fury.io/js/simple-command-bus) 3 | [![Build Status](https://travis-ci.org/erickjth/simple-command-bus.png?branch=master)](https://travis-ci.org/erickjth/simple-command-bus) 4 | [![codecov](https://codecov.io/gh/erickjth/simple-command-bus/branch/master/graph/badge.svg)](https://codecov.io/gh/erickjth/simple-command-bus) 5 | 6 | 7 | Simple Command Bus Implementation for NodeJS. 8 | It is majorly inspired by Tactician Command Bus for PHP https://tactician.thephpleague.com/ 9 | 10 | ## Requirements 11 | This project requires nodejs 8 or higher. 12 | 13 | ## Install 14 | ### NPM 15 | `npm install simple-command-bus` 16 | ### Yarn 17 | `yarn add simple-command-bus` 18 | 19 | ## Basic Usage 20 | ``` 21 | const { 22 | Command, 23 | CommandBus, 24 | CommandHandlerMiddleware, 25 | ClassNameExtractor, 26 | InMemoryLocator, 27 | HandleInflector, 28 | LoggerMiddleware  29 | } = require('simple-command-bus'); 30 | 31 | // CreateAccount Command 32 | class CreateAccountCommand extends Command { 33 | constructor(firstName, lastName) { 34 | super(); 35 | this.firstName = firstName; 36 | this.lastName = lastName; 37 | } 38 | } 39 | 40 | // CreateAccount Handler 41 | class CreateAccountHandler { 42 | handle(command) { 43 | // Logic to create an account. 44 | } 45 | }; 46 | 47 | // Handler middleware 48 | var commandHandlerMiddleware = new CommandHandlerMiddleware( 49 | new ClassNameExtractor(), 50 | new InMemoryLocator({ CreateAccountHandler: new CreateAccountHandler() }), 51 | new HandleInflector() 52 | ); 53 | 54 | // Command bus instance 55 | var commandBus = new CommandBus([ 56 | new LoggerMiddleware(console), 57 | commandHandlerMiddleware 58 | ]); 59 | 60 | const createAccountCommand = new CreateAccountCommand('John', 'Doe'); 61 | var result = commandBus.handle(createAccountCommand); 62 | console.log('Result:', result); 63 | ``` 64 | 65 | ## Run tests 66 | `yarn run test` 67 | 68 | ## Run tests with coverage 69 | `yarn run test:coverage` 70 | 71 | ## Check example 72 | - `node examples/index.js` 73 | -------------------------------------------------------------------------------- /examples/error.js: -------------------------------------------------------------------------------- 1 | 2 | const util = require('util'); 3 | const CommandBus = require('../lib/CommandBus').default; 4 | const Command = require('../lib/Command').default; 5 | const InvalidMiddlewareException = require('../lib/exceptions/InvalidMiddleware').default; 6 | 7 | class FooCommand extends Command { 8 | } 9 | 10 | class InvalidMiddleware {} 11 | 12 | var commandBus = new CommandBus([ new InvalidMiddleware() ]); 13 | 14 | //////////////////////////////// 15 | const fooCommand = new FooCommand(); 16 | 17 | try { 18 | var result = commandBus.handle(fooCommand); 19 | } catch (e) { 20 | console.log(e.message()); 21 | } 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | 2 | const util = require('util'); 3 | const CommandBus = require('../lib/CommandBus').default; 4 | const CommandHandlerMiddleware = require('../lib/handler/CommandHandlerMiddleware').default; 5 | const LoggerMiddleware = require('../lib/plugins/LoggerMiddleware').default; 6 | const ClassNameExtractor = require('../lib/handler/CommandNameExtractor/ClassNameExtractor').default; 7 | const HandleInflector = require('../lib/handler/MethodNameInflector/HandleInflector').default; 8 | const InMemoryLocator = require('../lib/handler/Locator/InMemoryLocator').default; 9 | const Command = require('../lib/Command').default; 10 | 11 | ////////////////////// 12 | class CreateAccountCommand extends Command { 13 | constructor(name, last) { 14 | super(); 15 | this.name = name; 16 | this.last = last; 17 | } 18 | } 19 | 20 | class CreateAccountHandler { 21 | handle(command) { 22 | return new Promise(resolve => 23 | setTimeout(() => resolve({ 24 | name: command.name, 25 | last: command.last 26 | }), 300) 27 | ); 28 | } 29 | }; 30 | 31 | ///////////////////////// 32 | var commandHandlerMiddleware = new CommandHandlerMiddleware( 33 | new ClassNameExtractor(), 34 | new InMemoryLocator({ CreateAccountHandler: new CreateAccountHandler() }), 35 | new HandleInflector() 36 | ); 37 | 38 | var commandBus = new CommandBus([ 39 | new LoggerMiddleware(console), 40 | commandHandlerMiddleware 41 | ]); 42 | 43 | //////////////////////////////// 44 | 45 | (async function() { 46 | const createAccountCommand = new CreateAccountCommand('John', 'Doe'); 47 | 48 | try { 49 | var result = await commandBus.handle(createAccountCommand); 50 | console.log('Result:', result); 51 | } catch(e) { 52 | console.log('Something went wrong', e); 53 | } 54 | })() 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /examples/proxy/commands/FooCommand.js: -------------------------------------------------------------------------------- 1 | const Command = require('../../../lib/Command').default; 2 | 3 | class FooCommand extends Command { 4 | constructor(bar, baz) { 5 | super(); 6 | this.bar = bar; 7 | this.baz = baz; 8 | } 9 | } 10 | 11 | module.exports = FooCommand; 12 | -------------------------------------------------------------------------------- /examples/proxy/handlers/FooHandler.js: -------------------------------------------------------------------------------- 1 | class FooHandler { 2 | handle(command) { 3 | return { 4 | bar: command.bar, 5 | baz: command.baz, 6 | } 7 | } 8 | }; 9 | 10 | module.exports = FooHandler; 11 | -------------------------------------------------------------------------------- /examples/proxy/index.js: -------------------------------------------------------------------------------- 1 | const CommandBus = require('../../lib/CommandBus').default; 2 | const CreateCommandBusProxy = require('../../lib/CreateCommandBusProxy').default; 3 | const CommandHandlerMiddleware = require('../../lib/handler/CommandHandlerMiddleware').default; 4 | const LoggerMiddleware = require('../../lib/plugins/LoggerMiddleware').default; 5 | const ClassNameExtractor = require('../../lib/handler/CommandNameExtractor/ClassNameExtractor').default; 6 | const HandleInflector = require('../../lib/handler/MethodNameInflector/HandleInflector').default; 7 | const NamespaceHandlerLocator = require('../../lib/handler/Locator/NamespaceHandlerLocator').default; 8 | const MissingHandlerException = require('../../lib/exceptions/MissingHandlerException').default; 9 | 10 | const commandsPath = __dirname + '/commands'; 11 | const handlersPath = __dirname + '/handlers'; 12 | 13 | var commandHandlerMiddleware = new CommandHandlerMiddleware( 14 | new ClassNameExtractor(), 15 | new NamespaceHandlerLocator(handlersPath), 16 | new HandleInflector() 17 | ); 18 | 19 | var simpleCommandBus = new CommandBus([commandHandlerMiddleware]); 20 | 21 | const commandBusProxy = CreateCommandBusProxy(simpleCommandBus, commandsPath); 22 | 23 | const result = commandBusProxy.foo('John', 'Doe'); 24 | 25 | console.log(result); 26 | -------------------------------------------------------------------------------- /lib/Command.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9 | 10 | /** 11 | * Abstract class for a command 12 | */ 13 | var Command = function Command() { 14 | _classCallCheck(this, Command); 15 | }; 16 | 17 | exports["default"] = Command; -------------------------------------------------------------------------------- /lib/CommandBus.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _Middleware = _interopRequireDefault(require("./Middleware")); 9 | 10 | var _Command = _interopRequireDefault(require("./Command")); 11 | 12 | var _InvalidMiddlewareException = _interopRequireDefault(require("./exceptions/InvalidMiddlewareException")); 13 | 14 | var _InvalidCommandException = _interopRequireDefault(require("./exceptions/InvalidCommandException")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 17 | 18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19 | 20 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 21 | 22 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 23 | 24 | // Intend to define private property 25 | var stack = Symbol('stack'); 26 | /** 27 | * Bus that run and handle commands through middlewares 28 | */ 29 | 30 | var commandBus = 31 | /*#__PURE__*/ 32 | function () { 33 | function commandBus() { 34 | var middlewares = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 35 | 36 | _classCallCheck(this, commandBus); 37 | 38 | this[stack] = middlewares; 39 | } 40 | 41 | _createClass(commandBus, [{ 42 | key: "getMiddlewareStack", 43 | value: function getMiddlewareStack() { 44 | return this[stack]; 45 | } 46 | }, { 47 | key: "handle", 48 | value: function handle(command) { 49 | if (command instanceof _Command["default"] === false) { 50 | _InvalidCommandException["default"].forCommand(command); 51 | } 52 | 53 | var runCommandInMiddlewareStack = this[stack].reduceRight(function (next, middleware) { 54 | if (middleware instanceof _Middleware["default"] === false) { 55 | _InvalidMiddlewareException["default"].forMiddleware(middleware); 56 | } 57 | 58 | return middleware.execute.bind(middleware, command, next); 59 | }, function () { 60 | return null; 61 | }); 62 | var result = runCommandInMiddlewareStack(); 63 | return result; 64 | } 65 | }]); 66 | 67 | return commandBus; 68 | }(); 69 | 70 | exports["default"] = commandBus; -------------------------------------------------------------------------------- /lib/CommandInterface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8 | 9 | var Command = function Command() { 10 | _classCallCheck(this, Command); 11 | }; 12 | 13 | exports.default = Command; 14 | ; -------------------------------------------------------------------------------- /lib/CreateCommandBusProxy.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _utils = require("./utils"); 9 | 10 | function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } 11 | 12 | function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } 13 | 14 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 15 | 16 | var cachedCommands = {}; 17 | 18 | var CreateCommandBusProxy = function CreateCommandBusProxy(commandBus, commandsDir) { 19 | if (!commandsDir || !(0, _utils.isDirectory)(commandsDir)) { 20 | throw new Error('Invalid commands path.'); 21 | } 22 | 23 | var availableCommands = (0, _utils.walkSync)(commandsDir); 24 | return new Proxy({}, { 25 | get: function get(target, propKey) { 26 | var commandName = "".concat((0, _utils.upperFirst)((0, _utils.camelCase)(propKey)), "Command.js"); 27 | 28 | if (!cachedCommands[commandName]) { 29 | var foundCommand = availableCommands.find(function (command) { 30 | return command.endsWith(commandName); 31 | }); 32 | 33 | if (!foundCommand) { 34 | throw new Error("Command \"".concat(commandName, "\" not found.")); 35 | } 36 | 37 | cachedCommands[commandName] = require(foundCommand); // eslint-disable-line 38 | } 39 | 40 | var Command = cachedCommands[commandName]; 41 | 42 | if ((0, _utils.isFunction)(Command) === false) { 43 | throw new Error("Command \"".concat(commandName, "\" is not callable.")); 44 | } 45 | 46 | return function () { 47 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 48 | args[_key] = arguments[_key]; 49 | } 50 | 51 | return commandBus.handle(_construct(Command, args)); 52 | }; 53 | } 54 | }); 55 | }; 56 | 57 | var _default = CreateCommandBusProxy; 58 | exports["default"] = _default; -------------------------------------------------------------------------------- /lib/Middleware.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9 | 10 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 11 | 12 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 13 | 14 | /** 15 | * Abstract class for a middleware 16 | */ 17 | var Middleware = 18 | /*#__PURE__*/ 19 | function () { 20 | function Middleware() { 21 | _classCallCheck(this, Middleware); 22 | } 23 | 24 | _createClass(Middleware, [{ 25 | key: "execute", 26 | value: function execute(command, next) { 27 | throw new Error('execute method must be implemented'); 28 | } 29 | }]); 30 | 31 | return Middleware; 32 | }(); 33 | 34 | exports["default"] = Middleware; -------------------------------------------------------------------------------- /lib/exceptions/InvalidCommand.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _lodash = require('lodash'); 8 | 9 | var _createException = require('./createException'); 10 | 11 | var _createException2 = _interopRequireDefault(_createException); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | var InvalidCommand = (0, _createException2.default)('InvalidCommand', { 16 | message: 'Invalid Command' 17 | }); 18 | 19 | InvalidCommand.forCommand = function (command) { 20 | var message = null; 21 | 22 | if ((0, _lodash.isObject)(command)) { 23 | message = 'Command ' + command.constructor.name + ' is invalid. It must extend from Command.'; 24 | } 25 | 26 | throw new InvalidCommand(message); 27 | }; 28 | 29 | exports.default = InvalidCommand; -------------------------------------------------------------------------------- /lib/exceptions/InvalidCommandException.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _lodash = require("lodash"); 9 | 10 | var _createException = _interopRequireDefault(require("./createException")); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 13 | 14 | var InvalidCommandException = (0, _createException["default"])('InvalidCommandException', { 15 | message: 'Invalid Command' 16 | }); 17 | 18 | InvalidCommandException.forCommand = function (command) { 19 | var message = null; 20 | 21 | if ((0, _lodash.isObject)(command)) { 22 | message = "Command ".concat(command.constructor.name, " is invalid. It must extend from Command."); 23 | } 24 | 25 | throw new InvalidCommandException(message); 26 | }; 27 | 28 | var _default = InvalidCommandException; 29 | exports["default"] = _default; -------------------------------------------------------------------------------- /lib/exceptions/InvalidHandlerMethod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _createException = require('./createException'); 8 | 9 | var _createException2 = _interopRequireDefault(_createException); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | var InvalidHandlerMethod = (0, _createException2.default)('InvalidHandlerMethod', { 14 | message: 'Invalid handler method.' 15 | }); 16 | 17 | InvalidHandlerMethod.forMethod = function (method) { 18 | throw new InvalidHandlerMethod('Invalid handler method ' + method + '.'); 19 | }; 20 | 21 | exports.default = InvalidHandlerMethod; -------------------------------------------------------------------------------- /lib/exceptions/InvalidHandlerMethodException.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _createException = _interopRequireDefault(require("./createException")); 9 | 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 11 | 12 | var InvalidHandlerMethodException = (0, _createException["default"])('InvalidHandlerMethodException', { 13 | message: 'Invalid handler method.' 14 | }); 15 | 16 | InvalidHandlerMethodException.forMethod = function (method) { 17 | throw new InvalidHandlerMethodException("Invalid handler method ".concat(method, ".")); 18 | }; 19 | 20 | var _default = InvalidHandlerMethodException; 21 | exports["default"] = _default; -------------------------------------------------------------------------------- /lib/exceptions/InvalidMiddleware.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _lodash = require('lodash'); 8 | 9 | var _createException = require('./createException'); 10 | 11 | var _createException2 = _interopRequireDefault(_createException); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | var InvalidMiddleware = (0, _createException2.default)('InvalidMiddleware', { 16 | message: 'Invalid Middleware' 17 | }); 18 | 19 | InvalidMiddleware.forMiddleware = function (middleware) { 20 | var message = null; 21 | 22 | if ((0, _lodash.isObject)(middleware)) { 23 | message = 'Middleware ' + middleware.constructor.name + ' is invalid. It must extend from Middleware'; 24 | } 25 | 26 | throw new InvalidMiddleware(message); 27 | }; 28 | 29 | exports.default = InvalidMiddleware; -------------------------------------------------------------------------------- /lib/exceptions/InvalidMiddlewareException.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _lodash = require("lodash"); 9 | 10 | var _createException = _interopRequireDefault(require("./createException")); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 13 | 14 | var InvalidMiddlewareException = (0, _createException["default"])('InvalidMiddlewareException', { 15 | message: 'Invalid Middleware' 16 | }); 17 | 18 | InvalidMiddlewareException.forMiddleware = function (middleware) { 19 | var message = null; 20 | 21 | if ((0, _lodash.isObject)(middleware)) { 22 | message = "Middleware ".concat(middleware.constructor.name, " is invalid. It must extend from Middleware"); 23 | } 24 | 25 | throw new InvalidMiddlewareException(message); 26 | }; 27 | 28 | var _default = InvalidMiddlewareException; 29 | exports["default"] = _default; -------------------------------------------------------------------------------- /lib/exceptions/MissingHandler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _lodash = require('lodash'); 8 | 9 | var _createException = require('./createException'); 10 | 11 | var _createException2 = _interopRequireDefault(_createException); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | var MissingHandler = (0, _createException2.default)('MissingHandler', { 16 | message: 'Invalid Command' 17 | }); 18 | 19 | MissingHandler.forCommand = function (commandName) { 20 | var message = null; 21 | 22 | if ((0, _lodash.isString)(commandName)) { 23 | message = 'There is no a handler for "' + commandName + '" Command.'; 24 | } 25 | 26 | throw new MissingHandler(message); 27 | }; 28 | 29 | exports.default = MissingHandler; -------------------------------------------------------------------------------- /lib/exceptions/MissingHandlerException.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _lodash = require("lodash"); 9 | 10 | var _createException = _interopRequireDefault(require("./createException")); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 13 | 14 | var MissingHandlerException = (0, _createException["default"])('MissingHandlerException', { 15 | message: 'Invalid Command' 16 | }); 17 | 18 | MissingHandlerException.forCommand = function (commandName) { 19 | var message = null; 20 | 21 | if ((0, _lodash.isString)(commandName)) { 22 | message = "There is no a handler for \"".concat(commandName, "\" Command."); 23 | } 24 | 25 | throw new MissingHandlerException(message); 26 | }; 27 | 28 | var _default = MissingHandlerException; 29 | exports["default"] = _default; -------------------------------------------------------------------------------- /lib/exceptions/createException.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = createException; 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9 | 10 | function createException(name, options) { 11 | var Exception = function Exception(message, code) { 12 | _classCallCheck(this, Exception); 13 | 14 | if (Error.captureStackTrace) { 15 | Error.captureStackTrace(this, this.constructor); 16 | } else { 17 | this.stack = new Error().stack; 18 | } 19 | 20 | this.message = options.message || message; 21 | this.code = options.code || code; 22 | }; 23 | 24 | Exception.prototype = new Error(); 25 | Exception.prototype.name = name; 26 | Exception.prototype.type = 'Exception'; 27 | Exception.prototype.constructor = Exception; 28 | return Exception; 29 | } -------------------------------------------------------------------------------- /lib/handler/CommandHandlerMiddleware.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _lodash = require("lodash"); 9 | 10 | var _CommandNameExtractor = _interopRequireDefault(require("./CommandNameExtractor/CommandNameExtractor")); 11 | 12 | var _MethodNameInflector = _interopRequireDefault(require("./MethodNameInflector/MethodNameInflector")); 13 | 14 | var _HandlerLocator = _interopRequireDefault(require("./Locator/HandlerLocator")); 15 | 16 | var _Middleware2 = _interopRequireDefault(require("../Middleware")); 17 | 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 19 | 20 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 21 | 22 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 23 | 24 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 25 | 26 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 27 | 28 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 29 | 30 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 31 | 32 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 33 | 34 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 35 | 36 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 37 | 38 | // Intend to define private property 39 | var _commandNameExtractor = Symbol('commandNameExtractor'); 40 | 41 | var _handlerLocator = Symbol('handlerLocator'); 42 | 43 | var _methodNameInflector = Symbol('methodNameInflector'); 44 | 45 | var CommandHandlerMiddleware = 46 | /*#__PURE__*/ 47 | function (_Middleware) { 48 | _inherits(CommandHandlerMiddleware, _Middleware); 49 | 50 | function CommandHandlerMiddleware(commandNameExtractor, handlerLocator, methodNameInflector) { 51 | var _this; 52 | 53 | _classCallCheck(this, CommandHandlerMiddleware); 54 | 55 | _this = _possibleConstructorReturn(this, _getPrototypeOf(CommandHandlerMiddleware).call(this)); 56 | _this[_commandNameExtractor] = commandNameExtractor; 57 | _this[_handlerLocator] = handlerLocator; 58 | _this[_methodNameInflector] = methodNameInflector; 59 | return _this; 60 | } 61 | 62 | _createClass(CommandHandlerMiddleware, [{ 63 | key: "execute", 64 | value: function execute(command, next) { 65 | var commandName = null; 66 | var handler = null; 67 | var methodName = null; 68 | var result = null; 69 | 70 | if (this[_commandNameExtractor] instanceof _CommandNameExtractor["default"]) { 71 | commandName = this[_commandNameExtractor].extractName(command); 72 | } 73 | 74 | if (commandName && this[_handlerLocator] instanceof _HandlerLocator["default"]) { 75 | handler = this[_handlerLocator].getHandlerForCommand(commandName); 76 | } 77 | 78 | if (commandName && handler && this[_methodNameInflector] instanceof _MethodNameInflector["default"]) { 79 | methodName = this[_methodNameInflector].inflect(commandName, handler); 80 | } 81 | 82 | if (handler && (0, _lodash.isFunction)(handler[methodName])) { 83 | result = handler[methodName].call(handler, command); 84 | } 85 | 86 | return result || null; 87 | } 88 | }, { 89 | key: "commandNameExtractor", 90 | set: function set(commandNameExtractor) { 91 | this[_commandNameExtractor] = commandNameExtractor; 92 | } 93 | }, { 94 | key: "handlerLocator", 95 | set: function set(handlerLocator) { 96 | this[_handlerLocator] = handlerLocator; 97 | } 98 | }, { 99 | key: "methodNameInflector", 100 | set: function set(methodNameInflector) { 101 | this[_methodNameInflector] = methodNameInflector; 102 | } 103 | }]); 104 | 105 | return CommandHandlerMiddleware; 106 | }(_Middleware2["default"]); 107 | 108 | exports["default"] = CommandHandlerMiddleware; -------------------------------------------------------------------------------- /lib/handler/CommandNameExtractor/ClassNameExtractor.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _lodash = require("lodash"); 9 | 10 | var _CommandNameExtractor2 = _interopRequireDefault(require("./CommandNameExtractor")); 11 | 12 | var _InvalidCommandException = _interopRequireDefault(require("../../exceptions/InvalidCommandException")); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 15 | 16 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 17 | 18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19 | 20 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 21 | 22 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 23 | 24 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 25 | 26 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 27 | 28 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 29 | 30 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 31 | 32 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 33 | 34 | var ClassNameExtractor = 35 | /*#__PURE__*/ 36 | function (_CommandNameExtractor) { 37 | _inherits(ClassNameExtractor, _CommandNameExtractor); 38 | 39 | function ClassNameExtractor() { 40 | _classCallCheck(this, ClassNameExtractor); 41 | 42 | return _possibleConstructorReturn(this, _getPrototypeOf(ClassNameExtractor).apply(this, arguments)); 43 | } 44 | 45 | _createClass(ClassNameExtractor, [{ 46 | key: "extractName", 47 | value: function extractName(command) { 48 | if ((0, _lodash.isObject)(command) === false || (0, _lodash.isString)(command.constructor.name) === false) { 49 | throw new _InvalidCommandException["default"]('Invalid Command Name.'); 50 | } 51 | 52 | return command.constructor.name; 53 | } 54 | }]); 55 | 56 | return ClassNameExtractor; 57 | }(_CommandNameExtractor2["default"]); 58 | 59 | exports["default"] = ClassNameExtractor; -------------------------------------------------------------------------------- /lib/handler/CommandNameExtractor/CommandNameExtractor.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9 | 10 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 11 | 12 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 13 | 14 | var CommandNameExtractor = 15 | /*#__PURE__*/ 16 | function () { 17 | function CommandNameExtractor() { 18 | _classCallCheck(this, CommandNameExtractor); 19 | } 20 | 21 | _createClass(CommandNameExtractor, [{ 22 | key: "extractName", 23 | value: function extractName(command) { 24 | throw new Error('extractName method must be implemented'); 25 | } 26 | }]); 27 | 28 | return CommandNameExtractor; 29 | }(); 30 | 31 | exports["default"] = CommandNameExtractor; -------------------------------------------------------------------------------- /lib/handler/Locator/HandlerLocator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9 | 10 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 11 | 12 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 13 | 14 | var HandlerLocator = 15 | /*#__PURE__*/ 16 | function () { 17 | function HandlerLocator() { 18 | _classCallCheck(this, HandlerLocator); 19 | } 20 | 21 | _createClass(HandlerLocator, [{ 22 | key: "getHandlerForCommand", 23 | value: function getHandlerForCommand(command) { 24 | throw new Error('getHandlerForCommand method must be implemented'); 25 | } 26 | }]); 27 | 28 | return HandlerLocator; 29 | }(); 30 | 31 | exports["default"] = HandlerLocator; -------------------------------------------------------------------------------- /lib/handler/Locator/InMemoryLocator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _lodash = require("lodash"); 9 | 10 | var _HandlerLocator2 = _interopRequireDefault(require("./HandlerLocator")); 11 | 12 | var _MissingHandlerException = _interopRequireDefault(require("../../exceptions/MissingHandlerException")); 13 | 14 | var _InvalidCommandException = _interopRequireDefault(require("../../exceptions/InvalidCommandException")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 17 | 18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 19 | 20 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 21 | 22 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 23 | 24 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 25 | 26 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 27 | 28 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 29 | 30 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 31 | 32 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 33 | 34 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 35 | 36 | var InMemoryLocator = 37 | /*#__PURE__*/ 38 | function (_HandlerLocator) { 39 | _inherits(InMemoryLocator, _HandlerLocator); 40 | 41 | function InMemoryLocator() { 42 | var _this; 43 | 44 | var handlers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 45 | 46 | _classCallCheck(this, InMemoryLocator); 47 | 48 | _this = _possibleConstructorReturn(this, _getPrototypeOf(InMemoryLocator).call(this)); 49 | _this.handlers = {}; 50 | 51 | if ((0, _lodash.isObject)(handlers)) { 52 | _this.handlers = (0, _lodash.reduce)(handlers, function (carry, Handler, key) { 53 | carry[key] = (0, _lodash.isFunction)(Handler) ? new Handler() : Handler; // eslint-disable-line 54 | 55 | return carry; 56 | }, {}); 57 | } 58 | 59 | return _this; 60 | } 61 | 62 | _createClass(InMemoryLocator, [{ 63 | key: "getHandlerForCommand", 64 | value: function getHandlerForCommand(commandName) { 65 | if ((0, _lodash.isString)(commandName) === false) { 66 | throw new _InvalidCommandException["default"](); 67 | } 68 | 69 | var handlerName = commandName.replace('Command', 'Handler'); 70 | 71 | if ((0, _lodash.has)(this.handlers, handlerName) === false) { 72 | _MissingHandlerException["default"].forCommand(commandName); 73 | } 74 | 75 | return this.handlers[handlerName]; 76 | } 77 | }]); 78 | 79 | return InMemoryLocator; 80 | }(_HandlerLocator2["default"]); 81 | 82 | exports["default"] = InMemoryLocator; -------------------------------------------------------------------------------- /lib/handler/Locator/NamespaceHandlerLocator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _utils = require("../../utils"); 9 | 10 | var _HandlerLocator2 = _interopRequireDefault(require("./HandlerLocator")); 11 | 12 | var _MissingHandlerException = _interopRequireDefault(require("../../exceptions/MissingHandlerException")); 13 | 14 | var _InvalidCommandException = _interopRequireDefault(require("../../exceptions/InvalidCommandException")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 17 | 18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 19 | 20 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 21 | 22 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 23 | 24 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 25 | 26 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 27 | 28 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 29 | 30 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 31 | 32 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 33 | 34 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 35 | 36 | var NamespaceHandlerLocator = 37 | /*#__PURE__*/ 38 | function (_HandlerLocator) { 39 | _inherits(NamespaceHandlerLocator, _HandlerLocator); 40 | 41 | function NamespaceHandlerLocator(handlersPath) { 42 | var _this; 43 | 44 | _classCallCheck(this, NamespaceHandlerLocator); 45 | 46 | _this = _possibleConstructorReturn(this, _getPrototypeOf(NamespaceHandlerLocator).call(this)); 47 | 48 | if (!handlersPath || !(0, _utils.isDirectory)(handlersPath)) { 49 | throw new Error('Invalid commands path.'); 50 | } 51 | 52 | _this.handlers = (0, _utils.walkSync)(handlersPath); 53 | return _this; 54 | } 55 | 56 | _createClass(NamespaceHandlerLocator, [{ 57 | key: "getHandlerForCommand", 58 | value: function getHandlerForCommand(commandName) { 59 | if ((0, _utils.isString)(commandName) === false) { 60 | throw new _InvalidCommandException["default"](); 61 | } 62 | 63 | var handlerName = "".concat(commandName.replace('Command', 'Handler'), ".js"); 64 | var foundHandler = this.handlers.find(function (handler) { 65 | return handler.endsWith(handlerName); 66 | }); 67 | 68 | if (!foundHandler) { 69 | _MissingHandlerException["default"].forCommand(commandName); 70 | } 71 | 72 | var Handler = require(foundHandler); 73 | 74 | if ((0, _utils.isFunction)(Handler) === false) { 75 | _MissingHandlerException["default"].forCommand(commandName); 76 | } 77 | 78 | return new Handler(); 79 | } 80 | }]); 81 | 82 | return NamespaceHandlerLocator; 83 | }(_HandlerLocator2["default"]); 84 | 85 | exports["default"] = NamespaceHandlerLocator; -------------------------------------------------------------------------------- /lib/handler/MethodNameInflector/HandleInflector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _lodash = require("lodash"); 9 | 10 | var _MethodNameInflector2 = _interopRequireDefault(require("./MethodNameInflector")); 11 | 12 | var _InvalidHandlerMethodException = _interopRequireDefault(require("../../exceptions/InvalidHandlerMethodException")); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 15 | 16 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 17 | 18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19 | 20 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 21 | 22 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 23 | 24 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 25 | 26 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 27 | 28 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 29 | 30 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 31 | 32 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 33 | 34 | var HandleInflector = 35 | /*#__PURE__*/ 36 | function (_MethodNameInflector) { 37 | _inherits(HandleInflector, _MethodNameInflector); 38 | 39 | function HandleInflector(methodName) { 40 | var _this; 41 | 42 | _classCallCheck(this, HandleInflector); 43 | 44 | _this = _possibleConstructorReturn(this, _getPrototypeOf(HandleInflector).call(this)); 45 | _this.methodName = methodName || 'handle'; 46 | return _this; 47 | } 48 | 49 | _createClass(HandleInflector, [{ 50 | key: "inflect", 51 | value: function inflect(commandName, handler) { 52 | if ((0, _lodash.isFunction)(handler[this.methodName]) === false) { 53 | _InvalidHandlerMethodException["default"].forMethod(this.methodName); 54 | } 55 | 56 | return this.methodName; 57 | } 58 | }]); 59 | 60 | return HandleInflector; 61 | }(_MethodNameInflector2["default"]); 62 | 63 | exports["default"] = HandleInflector; -------------------------------------------------------------------------------- /lib/handler/MethodNameInflector/MethodNameInflector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9 | 10 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 11 | 12 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 13 | 14 | var MethodNameInflector = 15 | /*#__PURE__*/ 16 | function () { 17 | function MethodNameInflector() { 18 | _classCallCheck(this, MethodNameInflector); 19 | } 20 | 21 | _createClass(MethodNameInflector, [{ 22 | key: "inflect", 23 | value: function inflect(command) { 24 | throw new Error('inflect method must be implemented'); 25 | } 26 | }]); 27 | 28 | return MethodNameInflector; 29 | }(); 30 | 31 | exports["default"] = MethodNameInflector; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "Middleware", { 7 | enumerable: true, 8 | get: function get() { 9 | return _Middleware["default"]; 10 | } 11 | }); 12 | Object.defineProperty(exports, "Command", { 13 | enumerable: true, 14 | get: function get() { 15 | return _Command["default"]; 16 | } 17 | }); 18 | Object.defineProperty(exports, "CommandBus", { 19 | enumerable: true, 20 | get: function get() { 21 | return _CommandBus["default"]; 22 | } 23 | }); 24 | Object.defineProperty(exports, "CreateCommandBusProxy", { 25 | enumerable: true, 26 | get: function get() { 27 | return _CreateCommandBusProxy["default"]; 28 | } 29 | }); 30 | Object.defineProperty(exports, "InvalidMiddlewareException", { 31 | enumerable: true, 32 | get: function get() { 33 | return _InvalidMiddlewareException["default"]; 34 | } 35 | }); 36 | Object.defineProperty(exports, "InvalidCommandException", { 37 | enumerable: true, 38 | get: function get() { 39 | return _InvalidCommandException["default"]; 40 | } 41 | }); 42 | Object.defineProperty(exports, "InvalidHandlerMethodException", { 43 | enumerable: true, 44 | get: function get() { 45 | return _InvalidHandlerMethodException["default"]; 46 | } 47 | }); 48 | Object.defineProperty(exports, "MissingHandlerException", { 49 | enumerable: true, 50 | get: function get() { 51 | return _MissingHandlerException["default"]; 52 | } 53 | }); 54 | Object.defineProperty(exports, "LoggerMiddleware", { 55 | enumerable: true, 56 | get: function get() { 57 | return _LoggerMiddleware["default"]; 58 | } 59 | }); 60 | Object.defineProperty(exports, "CommandHandlerMiddleware", { 61 | enumerable: true, 62 | get: function get() { 63 | return _CommandHandlerMiddleware["default"]; 64 | } 65 | }); 66 | Object.defineProperty(exports, "CommandNameExtractor", { 67 | enumerable: true, 68 | get: function get() { 69 | return _CommandNameExtractor["default"]; 70 | } 71 | }); 72 | Object.defineProperty(exports, "MethodNameInflector", { 73 | enumerable: true, 74 | get: function get() { 75 | return _MethodNameInflector["default"]; 76 | } 77 | }); 78 | Object.defineProperty(exports, "HandlerLocator", { 79 | enumerable: true, 80 | get: function get() { 81 | return _HandlerLocator["default"]; 82 | } 83 | }); 84 | Object.defineProperty(exports, "ClassNameExtractor", { 85 | enumerable: true, 86 | get: function get() { 87 | return _ClassNameExtractor["default"]; 88 | } 89 | }); 90 | Object.defineProperty(exports, "HandleInflector", { 91 | enumerable: true, 92 | get: function get() { 93 | return _HandleInflector["default"]; 94 | } 95 | }); 96 | Object.defineProperty(exports, "InMemoryLocator", { 97 | enumerable: true, 98 | get: function get() { 99 | return _InMemoryLocator["default"]; 100 | } 101 | }); 102 | Object.defineProperty(exports, "NamespaceHandlerLocator", { 103 | enumerable: true, 104 | get: function get() { 105 | return _NamespaceHandlerLocator["default"]; 106 | } 107 | }); 108 | exports["default"] = void 0; 109 | 110 | var _Middleware = _interopRequireDefault(require("./Middleware")); 111 | 112 | var _Command = _interopRequireDefault(require("./Command")); 113 | 114 | var _CommandBus = _interopRequireDefault(require("./CommandBus")); 115 | 116 | var _CreateCommandBusProxy = _interopRequireDefault(require("./CreateCommandBusProxy")); 117 | 118 | var _InvalidMiddlewareException = _interopRequireDefault(require("./exceptions/InvalidMiddlewareException")); 119 | 120 | var _InvalidCommandException = _interopRequireDefault(require("./exceptions/InvalidCommandException")); 121 | 122 | var _InvalidHandlerMethodException = _interopRequireDefault(require("./exceptions/InvalidHandlerMethodException")); 123 | 124 | var _MissingHandlerException = _interopRequireDefault(require("./exceptions/MissingHandlerException")); 125 | 126 | var _LoggerMiddleware = _interopRequireDefault(require("./plugins/LoggerMiddleware")); 127 | 128 | var _CommandHandlerMiddleware = _interopRequireDefault(require("./handler/CommandHandlerMiddleware")); 129 | 130 | var _CommandNameExtractor = _interopRequireDefault(require("./handler/CommandNameExtractor/CommandNameExtractor")); 131 | 132 | var _MethodNameInflector = _interopRequireDefault(require("./handler/MethodNameInflector/MethodNameInflector")); 133 | 134 | var _HandlerLocator = _interopRequireDefault(require("./handler/Locator/HandlerLocator")); 135 | 136 | var _ClassNameExtractor = _interopRequireDefault(require("./handler/CommandNameExtractor/ClassNameExtractor")); 137 | 138 | var _HandleInflector = _interopRequireDefault(require("./handler/MethodNameInflector/HandleInflector")); 139 | 140 | var _InMemoryLocator = _interopRequireDefault(require("./handler/Locator/InMemoryLocator")); 141 | 142 | var _NamespaceHandlerLocator = _interopRequireDefault(require("./handler/Locator/NamespaceHandlerLocator")); 143 | 144 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 145 | 146 | var _default = _CommandBus["default"]; 147 | exports["default"] = _default; -------------------------------------------------------------------------------- /lib/plugins/LoggerMiddleware.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _Middleware2 = _interopRequireDefault(require("../Middleware")); 9 | 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 11 | 12 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 17 | 18 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 19 | 20 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 21 | 22 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 23 | 24 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 25 | 26 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 27 | 28 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 29 | 30 | var LoggerMiddleware = 31 | /*#__PURE__*/ 32 | function (_Middleware) { 33 | _inherits(LoggerMiddleware, _Middleware); 34 | 35 | function LoggerMiddleware(logger) { 36 | var _this; 37 | 38 | _classCallCheck(this, LoggerMiddleware); 39 | 40 | _this = _possibleConstructorReturn(this, _getPrototypeOf(LoggerMiddleware).call(this)); 41 | _this.logger = logger; 42 | return _this; 43 | } 44 | 45 | _createClass(LoggerMiddleware, [{ 46 | key: "execute", 47 | value: function execute(command, next) { 48 | this.logger.log('Before command: ', command); 49 | var returnValue = next(command); 50 | this.logger.log('After command result: ', command, returnValue); 51 | return returnValue; 52 | } 53 | }]); 54 | 55 | return LoggerMiddleware; 56 | }(_Middleware2["default"]); 57 | 58 | exports["default"] = LoggerMiddleware; -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.isFunction = exports.isString = exports.upperFirst = exports.camelCase = exports.capitalize = exports.walkSync = exports.isDirectory = void 0; 7 | 8 | var _fs = _interopRequireDefault(require("fs")); 9 | 10 | var _path = _interopRequireDefault(require("path")); 11 | 12 | var _capitalize = _interopRequireDefault(require("lodash/capitalize")); 13 | 14 | var _camelCase = _interopRequireDefault(require("lodash/camelCase")); 15 | 16 | var _upperFirst = _interopRequireDefault(require("lodash/upperFirst")); 17 | 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 19 | 20 | var isDirectory = function isDirectory(dir) { 21 | return _fs["default"].lstatSync(dir).isDirectory(); 22 | }; 23 | 24 | exports.isDirectory = isDirectory; 25 | 26 | var walkSync = function walkSync(file) { 27 | return isDirectory(file) ? _fs["default"].readdirSync(file).map(function (f) { 28 | return walkSync(_path["default"].join(file, f)); 29 | }) : file; 30 | }; 31 | 32 | exports.walkSync = walkSync; 33 | 34 | var capitalize = function capitalize(s) { 35 | return (0, _capitalize["default"])(s); 36 | }; 37 | 38 | exports.capitalize = capitalize; 39 | 40 | var camelCase = function camelCase(s) { 41 | return (0, _camelCase["default"])(s); 42 | }; 43 | 44 | exports.camelCase = camelCase; 45 | 46 | var upperFirst = function upperFirst(s) { 47 | return (0, _upperFirst["default"])(s); 48 | }; 49 | 50 | exports.upperFirst = upperFirst; 51 | 52 | var isString = function isString(s) { 53 | return typeof s === 'string'; 54 | }; 55 | 56 | exports.isString = isString; 57 | 58 | var isFunction = function isFunction(f) { 59 | return typeof f === 'function'; 60 | }; 61 | 62 | exports.isFunction = isFunction; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-command-bus", 3 | "version": "1.0.7", 4 | "description": "Simple Command Bus", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build": "babel src/ -d lib/", 8 | "build:watch": "babel --watch src/ -d lib/", 9 | "typecheck": "flow check", 10 | "test": "mocha --recursive --require @babel/register", 11 | "test:coverage": "nyc --reporter=html --reporter=text mocha --recursive --require @babel/register", 12 | "report:coverage": "nyc --reporter=text --reporter=text-lcov mocha --recursive --require @babel/register > coverage.lcov && codecov" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/erickjth/simple-command-bus.git" 17 | }, 18 | "keywords": [ 19 | "bus", 20 | "command-bus", 21 | "command", 22 | "handlers" 23 | ], 24 | "author": "Erick Torres ", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/erickjth/simple-command-bus/issues" 28 | }, 29 | "homepage": "https://github.com/erickjth/simple-command-bus", 30 | "devDependencies": { 31 | "@babel/cli": "^7.4.4", 32 | "@babel/core": "^7.4.4", 33 | "@babel/node": "^7.2.2", 34 | "@babel/preset-env": "^7.4.4", 35 | "@babel/preset-flow": "^7.0.0", 36 | "chai": "^4.1.2", 37 | "codecov": "^3.0.1", 38 | "eslint-config-airbnb-standard": "^3.0.1", 39 | "flow-bin": "^0.70.0", 40 | "mocha": "^5.1.1", 41 | "mock-require": "^3.0.2", 42 | "nyc": "^13.3.0", 43 | "sinon": "^5.0.1" 44 | }, 45 | "dependencies": { 46 | "lodash": "^4.17.5" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Command.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Abstract class for a command 3 | */ 4 | export default class Command { 5 | } 6 | -------------------------------------------------------------------------------- /src/CommandBus.js: -------------------------------------------------------------------------------- 1 | import Middleware from './Middleware'; 2 | import Command from './Command'; 3 | import InvalidMiddlewareException from './exceptions/InvalidMiddlewareException'; 4 | import InvalidCommandException from './exceptions/InvalidCommandException'; 5 | 6 | // Intend to define private property 7 | const stack = Symbol('stack'); 8 | 9 | /** 10 | * Bus that run and handle commands through middlewares 11 | */ 12 | export default class commandBus { 13 | constructor(middlewares = []) { 14 | this[stack] = middlewares; 15 | } 16 | 17 | getMiddlewareStack() { 18 | return this[stack]; 19 | } 20 | 21 | handle(command) { 22 | if (command instanceof Command === false) { 23 | InvalidCommandException.forCommand(command); 24 | } 25 | 26 | const runCommandInMiddlewareStack = this[stack].reduceRight( 27 | (next, middleware) => { 28 | if (middleware instanceof Middleware === false) { 29 | InvalidMiddlewareException.forMiddleware(middleware); 30 | } 31 | 32 | return middleware.execute.bind(middleware, command, next); 33 | }, 34 | () => null 35 | ); 36 | 37 | const result = runCommandInMiddlewareStack(); 38 | 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/CreateCommandBusProxy.js: -------------------------------------------------------------------------------- 1 | import { camelCase, upperFirst, isDirectory, isFunction, walkSync } from './utils'; 2 | 3 | const cachedCommands = {}; 4 | 5 | const CreateCommandBusProxy = function CreateCommandBusProxy(commandBus, commandsDir) { 6 | if (!commandsDir || !isDirectory(commandsDir)) { 7 | throw new Error('Invalid commands path.'); 8 | } 9 | 10 | const availableCommands = walkSync(commandsDir); 11 | 12 | return new Proxy({}, { 13 | get(target, propKey) { 14 | const commandName = `${upperFirst(camelCase(propKey))}Command.js`; 15 | 16 | if (!cachedCommands[commandName]) { 17 | const foundCommand = availableCommands.find(command => command.endsWith(commandName)); 18 | 19 | if (!foundCommand) { 20 | throw new Error(`Command "${commandName}" not found.`); 21 | } 22 | 23 | cachedCommands[commandName] = require(foundCommand); // eslint-disable-line 24 | } 25 | 26 | const Command = cachedCommands[commandName]; 27 | 28 | if (isFunction(Command) === false) { 29 | throw new Error(`Command "${commandName}" is not callable.`); 30 | } 31 | 32 | return (...args) => commandBus.handle(new Command(...args)); 33 | } 34 | }); 35 | }; 36 | 37 | export default CreateCommandBusProxy; 38 | -------------------------------------------------------------------------------- /src/Middleware.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Abstract class for a middleware 3 | */ 4 | export default class Middleware { 5 | execute(command, next) { 6 | throw new Error('execute method must be implemented'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/exceptions/InvalidCommandException.js: -------------------------------------------------------------------------------- 1 | import { isObject } from 'lodash'; 2 | import createException from './createException'; 3 | 4 | const InvalidCommandException = createException('InvalidCommandException', { 5 | message: 'Invalid Command' 6 | }); 7 | 8 | InvalidCommandException.forCommand = (command) => { 9 | let message = null; 10 | 11 | if (isObject(command)) { 12 | message = `Command ${command.constructor.name} is invalid. It must extend from Command.`; 13 | } 14 | 15 | throw new InvalidCommandException(message); 16 | }; 17 | 18 | export default InvalidCommandException; 19 | -------------------------------------------------------------------------------- /src/exceptions/InvalidHandlerMethodException.js: -------------------------------------------------------------------------------- 1 | import createException from './createException'; 2 | 3 | const InvalidHandlerMethodException = createException('InvalidHandlerMethodException', { 4 | message: 'Invalid handler method.' 5 | }); 6 | 7 | InvalidHandlerMethodException.forMethod = (method) => { 8 | throw new InvalidHandlerMethodException(`Invalid handler method ${method}.`); 9 | }; 10 | 11 | export default InvalidHandlerMethodException; 12 | -------------------------------------------------------------------------------- /src/exceptions/InvalidMiddlewareException.js: -------------------------------------------------------------------------------- 1 | import { isObject } from 'lodash'; 2 | import createException from './createException'; 3 | 4 | const InvalidMiddlewareException = createException('InvalidMiddlewareException', { 5 | message: 'Invalid Middleware' 6 | }); 7 | 8 | InvalidMiddlewareException.forMiddleware = (middleware) => { 9 | let message = null; 10 | 11 | if (isObject(middleware)) { 12 | message = `Middleware ${middleware.constructor.name} is invalid. It must extend from Middleware`; 13 | } 14 | 15 | throw new InvalidMiddlewareException(message); 16 | }; 17 | 18 | export default InvalidMiddlewareException; 19 | -------------------------------------------------------------------------------- /src/exceptions/MissingHandlerException.js: -------------------------------------------------------------------------------- 1 | import { isString } from 'lodash'; 2 | import createException from './createException'; 3 | 4 | const MissingHandlerException = createException('MissingHandlerException', { 5 | message: 'Invalid Command' 6 | }); 7 | 8 | MissingHandlerException.forCommand = (commandName) => { 9 | let message = null; 10 | 11 | if (isString(commandName)) { 12 | message = `There is no a handler for "${commandName}" Command.`; 13 | } 14 | 15 | throw new MissingHandlerException(message); 16 | }; 17 | 18 | export default MissingHandlerException; 19 | -------------------------------------------------------------------------------- /src/exceptions/createException.js: -------------------------------------------------------------------------------- 1 | 2 | export default function createException(name, options) { 3 | class Exception { 4 | constructor(message, code) { 5 | if (Error.captureStackTrace) { 6 | Error.captureStackTrace(this, this.constructor); 7 | } else { 8 | this.stack = (new Error()).stack; 9 | } 10 | 11 | this.message = options.message || message; 12 | this.code = options.code || code; 13 | } 14 | } 15 | 16 | Exception.prototype = new Error(); 17 | Exception.prototype.name = name; 18 | Exception.prototype.type = 'Exception'; 19 | Exception.prototype.constructor = Exception; 20 | 21 | return Exception; 22 | } 23 | -------------------------------------------------------------------------------- /src/handler/CommandHandlerMiddleware.js: -------------------------------------------------------------------------------- 1 | import { isFunction } from 'lodash'; 2 | import CommandNameExtractor from './CommandNameExtractor/CommandNameExtractor'; 3 | import MethodNameInflector from './MethodNameInflector/MethodNameInflector'; 4 | import HandlerLocator from './Locator/HandlerLocator'; 5 | import Middleware from '../Middleware'; 6 | 7 | // Intend to define private property 8 | const _commandNameExtractor = Symbol('commandNameExtractor'); 9 | const _handlerLocator = Symbol('handlerLocator'); 10 | const _methodNameInflector = Symbol('methodNameInflector'); 11 | 12 | export default class CommandHandlerMiddleware extends Middleware { 13 | constructor(commandNameExtractor, handlerLocator, methodNameInflector) { 14 | super(); 15 | this[_commandNameExtractor] = commandNameExtractor; 16 | this[_handlerLocator] = handlerLocator; 17 | this[_methodNameInflector] = methodNameInflector; 18 | } 19 | 20 | set commandNameExtractor(commandNameExtractor) { 21 | this[_commandNameExtractor] = commandNameExtractor; 22 | } 23 | 24 | set handlerLocator(handlerLocator) { 25 | this[_handlerLocator] = handlerLocator; 26 | } 27 | 28 | set methodNameInflector(methodNameInflector) { 29 | this[_methodNameInflector] = methodNameInflector; 30 | } 31 | 32 | execute(command, next) { 33 | let commandName = null; 34 | let handler = null; 35 | let methodName = null; 36 | let result = null; 37 | 38 | if (this[_commandNameExtractor] instanceof CommandNameExtractor) { 39 | commandName = this[_commandNameExtractor].extractName(command); 40 | } 41 | 42 | if (commandName && this[_handlerLocator] instanceof HandlerLocator) { 43 | handler = this[_handlerLocator].getHandlerForCommand(commandName); 44 | } 45 | 46 | if (commandName && handler && this[_methodNameInflector] instanceof MethodNameInflector) { 47 | methodName = this[_methodNameInflector].inflect(commandName, handler); 48 | } 49 | 50 | if (handler && isFunction(handler[methodName])) { 51 | result = handler[methodName].call(handler, command); 52 | } 53 | 54 | return result || null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/handler/CommandNameExtractor/ClassNameExtractor.js: -------------------------------------------------------------------------------- 1 | import { has, isObject, isString } from 'lodash'; 2 | import CommandNameExtractor from './CommandNameExtractor'; 3 | import InvalidCommandException from '../../exceptions/InvalidCommandException'; 4 | 5 | export default class ClassNameExtractor extends CommandNameExtractor { 6 | extractName(command) { 7 | if (isObject(command) === false || 8 | isString(command.constructor.name) === false 9 | ) { 10 | throw new InvalidCommandException('Invalid Command Name.'); 11 | } 12 | 13 | return command.constructor.name; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/handler/CommandNameExtractor/CommandNameExtractor.js: -------------------------------------------------------------------------------- 1 | export default class CommandNameExtractor { 2 | extractName(command) { 3 | throw new Error('extractName method must be implemented'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/handler/Locator/HandlerLocator.js: -------------------------------------------------------------------------------- 1 | export default class HandlerLocator { 2 | getHandlerForCommand(command) { 3 | throw new Error('getHandlerForCommand method must be implemented'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/handler/Locator/InMemoryLocator.js: -------------------------------------------------------------------------------- 1 | import { reduce, has, isString, isFunction, isObject } from 'lodash'; 2 | import HandlerLocator from './HandlerLocator'; 3 | import MissingHandlerException from '../../exceptions/MissingHandlerException'; 4 | import InvalidCommandException from '../../exceptions/InvalidCommandException'; 5 | 6 | export default class InMemoryLocator extends HandlerLocator { 7 | constructor(handlers = {}) { 8 | super(); 9 | this.handlers = {}; 10 | if (isObject(handlers)) { 11 | this.handlers = reduce(handlers, (carry, Handler, key) => { 12 | carry[key] = isFunction(Handler) ? new Handler() : Handler; // eslint-disable-line 13 | return carry; 14 | }, {}); 15 | } 16 | } 17 | 18 | getHandlerForCommand(commandName) { 19 | if (isString(commandName) === false) { 20 | throw new InvalidCommandException(); 21 | } 22 | 23 | const handlerName = commandName.replace('Command', 'Handler'); 24 | 25 | if (has(this.handlers, handlerName) === false) { 26 | MissingHandlerException.forCommand(commandName); 27 | } 28 | 29 | return this.handlers[handlerName]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/handler/Locator/NamespaceHandlerLocator.js: -------------------------------------------------------------------------------- 1 | import { isString, isFunction, isDirectory, walkSync } from '../../utils'; 2 | import HandlerLocator from './HandlerLocator'; 3 | import MissingHandlerException from '../../exceptions/MissingHandlerException'; 4 | import InvalidCommandException from '../../exceptions/InvalidCommandException'; 5 | 6 | export default class NamespaceHandlerLocator extends HandlerLocator { 7 | constructor(handlersPath) { 8 | super(); 9 | 10 | if (!handlersPath || !isDirectory(handlersPath)) { 11 | throw new Error('Invalid commands path.'); 12 | } 13 | 14 | this.handlers = walkSync(handlersPath); 15 | } 16 | 17 | getHandlerForCommand(commandName) { 18 | if (isString(commandName) === false) { 19 | throw new InvalidCommandException(); 20 | } 21 | 22 | const handlerName = `${commandName.replace('Command', 'Handler')}.js`; 23 | const foundHandler = this.handlers.find(handler => handler.endsWith(handlerName)); 24 | 25 | if (!foundHandler) { 26 | MissingHandlerException.forCommand(commandName); 27 | } 28 | 29 | const Handler = require(foundHandler); 30 | 31 | if (isFunction(Handler) === false) { 32 | MissingHandlerException.forCommand(commandName); 33 | } 34 | 35 | return new Handler(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/handler/MethodNameInflector/HandleClassNameInflector.js: -------------------------------------------------------------------------------- 1 | import MethodNameInflector from './MethodNameInflector'; 2 | 3 | export default class HandleClassNameInflector extends MethodNameInflector { 4 | 5 | inflect(commandName, handler) { 6 | return 'handle' + commandName; 7 | } 8 | 9 | }; -------------------------------------------------------------------------------- /src/handler/MethodNameInflector/HandleInflector.js: -------------------------------------------------------------------------------- 1 | import { isFunction } from 'lodash'; 2 | import MethodNameInflector from './MethodNameInflector'; 3 | import InvalidHandlerMethodException from '../../exceptions/InvalidHandlerMethodException'; 4 | 5 | export default class HandleInflector extends MethodNameInflector { 6 | constructor(methodName) { 7 | super(); 8 | this.methodName = methodName || 'handle'; 9 | } 10 | 11 | inflect(commandName, handler) { 12 | if (isFunction(handler[this.methodName]) === false) { 13 | InvalidHandlerMethodException.forMethod(this.methodName); 14 | } 15 | 16 | return this.methodName; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/handler/MethodNameInflector/MethodNameInflector.js: -------------------------------------------------------------------------------- 1 | export default class MethodNameInflector { 2 | inflect(command) { 3 | throw new Error('inflect method must be implemented'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Middleware from './Middleware'; 2 | import Command from './Command'; 3 | import CommandBus from './CommandBus'; 4 | import CreateCommandBusProxy from './CreateCommandBusProxy'; 5 | import InvalidMiddlewareException from './exceptions/InvalidMiddlewareException'; 6 | import InvalidCommandException from './exceptions/InvalidCommandException'; 7 | import InvalidHandlerMethodException from './exceptions/InvalidHandlerMethodException'; 8 | import MissingHandlerException from './exceptions/MissingHandlerException'; 9 | import LoggerMiddleware from './plugins/LoggerMiddleware'; 10 | import CommandHandlerMiddleware from './handler/CommandHandlerMiddleware'; 11 | import CommandNameExtractor from './handler/CommandNameExtractor/CommandNameExtractor'; 12 | import MethodNameInflector from './handler/MethodNameInflector/MethodNameInflector'; 13 | import HandlerLocator from './handler/Locator/HandlerLocator'; 14 | import ClassNameExtractor from './handler/CommandNameExtractor/ClassNameExtractor'; 15 | import HandleInflector from './handler/MethodNameInflector/HandleInflector'; 16 | import InMemoryLocator from './handler/Locator/InMemoryLocator'; 17 | import NamespaceHandlerLocator from './handler/Locator/NamespaceHandlerLocator'; 18 | 19 | export default CommandBus; 20 | 21 | export { 22 | CommandBus, 23 | Middleware, 24 | Command, 25 | CreateCommandBusProxy, 26 | InvalidMiddlewareException, 27 | InvalidCommandException, 28 | InvalidHandlerMethodException, 29 | MissingHandlerException, 30 | CommandHandlerMiddleware, 31 | CommandNameExtractor, 32 | MethodNameInflector, 33 | HandlerLocator, 34 | LoggerMiddleware, 35 | ClassNameExtractor, 36 | HandleInflector, 37 | InMemoryLocator, 38 | NamespaceHandlerLocator 39 | }; 40 | -------------------------------------------------------------------------------- /src/plugins/LoggerMiddleware.js: -------------------------------------------------------------------------------- 1 | import Middleware from '../Middleware'; 2 | 3 | export default class LoggerMiddleware extends Middleware { 4 | constructor(logger) { 5 | super(); 6 | this.logger = logger; 7 | } 8 | 9 | execute(command, next) { 10 | this.logger.log('Before command: ', command); 11 | const returnValue = next(command); 12 | this.logger.log('After command result: ', command, returnValue); 13 | return returnValue; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import capitalizeStr from 'lodash/capitalize'; 4 | import camelCaseStr from 'lodash/camelCase'; 5 | import upperFirstStr from 'lodash/upperFirst'; 6 | 7 | const isDirectory = dir => fs.lstatSync(dir).isDirectory(); 8 | 9 | const walkSync = file => 10 | (isDirectory(file) ? fs.readdirSync(file).map(f => walkSync(path.join(file, f))) : file); 11 | 12 | const capitalize = s => capitalizeStr(s); 13 | 14 | const camelCase = s => camelCaseStr(s); 15 | 16 | const upperFirst = s => upperFirstStr(s); 17 | 18 | const isString = s => typeof s === 'string'; 19 | 20 | const isFunction = f => typeof f === 'function'; 21 | 22 | export { 23 | isDirectory, 24 | walkSync, 25 | capitalize, 26 | camelCase, 27 | upperFirst, 28 | isString, 29 | isFunction 30 | }; 31 | -------------------------------------------------------------------------------- /test/CommandBus.js: -------------------------------------------------------------------------------- 1 | /* global it, beforeEach, describe */ 2 | import { expect } from 'chai'; 3 | import CommandBus from '../src/CommandBus'; 4 | import Command from '../src/Command'; 5 | import CommandHandlerMiddleware from '../src/handler/CommandHandlerMiddleware'; 6 | import LoggerMiddleware from '../src/plugins/LoggerMiddleware'; 7 | import ClassNameExtractor from '../src/handler/CommandNameExtractor/ClassNameExtractor'; 8 | import HandleInflector from '../src/handler/MethodNameInflector/HandleInflector'; 9 | import HandleClassNameInflector from "../src/handler/MethodNameInflector/HandleClassNameInflector"; 10 | import InMemoryLocator from '../src/handler/Locator/InMemoryLocator'; 11 | 12 | const consoleMock = { 13 | log: () => null 14 | }; 15 | 16 | const commandHandlerMiddleware = new CommandHandlerMiddleware( 17 | new ClassNameExtractor(), 18 | new InMemoryLocator({}), 19 | new HandleInflector() 20 | ); 21 | 22 | 23 | const classNameHandlerMiddleware = new CommandHandlerMiddleware( 24 | new ClassNameExtractor(), 25 | new InMemoryLocator({}), 26 | new HandleClassNameInflector() 27 | ); 28 | 29 | describe('Testing CommandBus', function() { 30 | it('Testing constructor without middlewares', function() { 31 | const bus = new CommandBus(); 32 | expect(bus.getMiddlewareStack().length).to.be.equal(0); 33 | }); 34 | 35 | it('Testing constructor with middlewares', function() { 36 | const bus = new CommandBus([ 37 | new LoggerMiddleware(consoleMock) 38 | ]); 39 | 40 | expect(bus.getMiddlewareStack().length).to.be.equal(1); 41 | }); 42 | 43 | it('Handling a command without handler', function() { 44 | const bus = new CommandBus([]); 45 | class FooCommand extends Command {} 46 | const result = bus.handle(new FooCommand()); 47 | expect(result).to.be.equal(null); 48 | }); 49 | 50 | it('Handling a command with handler', function() { 51 | class SumCommand extends Command { 52 | constructor(a, b) { 53 | super(); 54 | this.a = a; 55 | this.b = b; 56 | } 57 | } 58 | 59 | class SumHandler { handle(command) { return command.a + command.b; } } 60 | commandHandlerMiddleware.handlerLocator = new InMemoryLocator({ SumHandler }); 61 | const bus = new CommandBus([commandHandlerMiddleware]); 62 | const sumCommand = new SumCommand(4, 6); 63 | const result = bus.handle(sumCommand); 64 | expect(result).to.be.equal(10); 65 | }); 66 | 67 | 68 | it('Handling a command with Class name inflector', function() { 69 | class SumCommand extends Command { 70 | constructor(a, b) { 71 | super(); 72 | this.a = a; 73 | this.b = b; 74 | } 75 | } 76 | 77 | class SumHandler { handleSumCommand(command) { return command.a + command.b; } } 78 | classNameHandlerMiddleware.handlerLocator = new InMemoryLocator({ SumHandler }); 79 | const bus = new CommandBus([classNameHandlerMiddleware]); 80 | const sumCommand = new SumCommand(4, 6); 81 | const result = bus.handle(sumCommand); 82 | expect(result).to.be.equal(10); 83 | }); 84 | 85 | it('Handling an invalid command', function() { 86 | class InvalidCommand {} 87 | const bus = new CommandBus(); 88 | expect(bus.handle.bind(null, new InvalidCommand())).to.throw(); 89 | }); 90 | 91 | it('Run command over invalid middleware', function() { 92 | class FooCommand extends Command {} 93 | 94 | class invalidMiddleware {} 95 | 96 | const bus = new CommandBus([ 97 | new invalidMiddleware() // Invalid middleware 98 | ]); 99 | 100 | expect(() => bus.handle(new FooCommand())).to.throw(); 101 | }) 102 | }); 103 | -------------------------------------------------------------------------------- /test/CreateCommandBusProxy.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import sinon from 'sinon'; 4 | import mock from 'mock-require'; 5 | import * as utils from '../src/utils'; 6 | import CommandBus from '../src/CommandBus'; 7 | import CreateCommandBusProxy from '../src/CreateCommandBusProxy'; 8 | import Command from '../src/Command'; 9 | import CommandHandlerMiddleware from '../src/handler/CommandHandlerMiddleware'; 10 | import ClassNameExtractor from '../src/handler/CommandNameExtractor/ClassNameExtractor'; 11 | import HandleInflector from '../src/handler/MethodNameInflector/HandleInflector'; 12 | import InMemoryLocator from '../src/handler/Locator/InMemoryLocator'; 13 | 14 | 15 | describe('Testing CommandBus with proxy', function() { 16 | it('Handling a command with handler', function() { 17 | 18 | sinon.stub(utils, 'walkSync').returns([ 'FooCommand.js' ]) 19 | sinon.stub(utils, 'isDirectory').returns(true); 20 | 21 | class FooCommand extends Command {} 22 | class FooHandler { handle() { return 1 } } 23 | 24 | mock('FooCommand.js', FooCommand); 25 | 26 | const bus = new CommandBus([ 27 | new CommandHandlerMiddleware( 28 | new ClassNameExtractor(), 29 | new InMemoryLocator({ FooHandler }), 30 | new HandleInflector() 31 | )]); 32 | 33 | const busProxy = CreateCommandBusProxy(bus, '/path/to/commands'); 34 | 35 | const result = busProxy.foo(); 36 | 37 | expect(result).to.be.equal(1); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/Middleware.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import Middleware from '../src/Middleware'; 4 | 5 | describe('Testing Abstract Middleware', () => { 6 | it('Abstract instance thrown an error', () => { 7 | const middleware = new Middleware(); 8 | expect(middleware.execute).to.throw(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/handler/CommandHandlerMiddleware.js: -------------------------------------------------------------------------------- 1 | /* global it, beforeEach, describe */ 2 | import { expect } from 'chai'; 3 | import CommandBus from '../../src/CommandBus'; 4 | import Command from '../../src/Command'; 5 | import CommandHandlerMiddleware from '../../src/handler/CommandHandlerMiddleware'; 6 | import ClassNameExtractor from '../../src/handler/CommandNameExtractor/ClassNameExtractor'; 7 | import HandleInflector from '../../src/handler/MethodNameInflector/HandleInflector'; 8 | import InMemoryLocator from '../../src/handler/Locator/InMemoryLocator'; 9 | 10 | const consoleMock = { log: () => null }; 11 | 12 | const commandHandlerMiddleware = new CommandHandlerMiddleware( 13 | new ClassNameExtractor(), 14 | new InMemoryLocator({}), 15 | new HandleInflector() 16 | ); 17 | 18 | describe('Testing CommandHandlerMiddleware', function() { 19 | it('Testing constructor with params', function() { 20 | const commandHandlerMiddleware = new CommandHandlerMiddleware( 21 | new ClassNameExtractor(), 22 | new InMemoryLocator({}), 23 | new HandleInflector() 24 | ); 25 | 26 | expect(commandHandlerMiddleware instanceof CommandHandlerMiddleware).to.be.true; 27 | }); 28 | 29 | it('Setting params separately', function() { 30 | const commandHandlerMiddleware = new CommandHandlerMiddleware(); 31 | commandHandlerMiddleware.commandNameExtractor = new ClassNameExtractor(); 32 | commandHandlerMiddleware.handlerLocator = new InMemoryLocator(); 33 | commandHandlerMiddleware.methodNameInflector = new HandleInflector(); 34 | 35 | expect(commandHandlerMiddleware instanceof CommandHandlerMiddleware).to.be.true; 36 | }); 37 | 38 | 39 | it('Handling a command', function() { 40 | class SumCommand extends Command { constructor(a, b) { super(); this.a = a; this.b = b; } } 41 | class SumHandler { handle(command) { return command.a + command.b; } } 42 | 43 | const commandHandlerMiddleware = new CommandHandlerMiddleware( 44 | new ClassNameExtractor(), 45 | new InMemoryLocator({ SumHandler: new SumHandler() }), 46 | new HandleInflector() 47 | ); 48 | 49 | const result = commandHandlerMiddleware.execute(new SumCommand(1, 3)); 50 | expect(result).to.be.equal(4); 51 | }); 52 | 53 | it('Handling a command without handler', function() { 54 | class SumCommand extends Command { constructor(a, b) { super(); this.a = a; this.b = b; } } 55 | 56 | const commandHandlerMiddleware = new CommandHandlerMiddleware( 57 | new ClassNameExtractor(), 58 | new InMemoryLocator({ }), 59 | new HandleInflector() 60 | ); 61 | 62 | expect(() => commandHandlerMiddleware.execute(new SumCommand(1, 2))).to.throw(); 63 | }); 64 | 65 | it('Handling a command without any lib', function() { 66 | class SumCommand extends Command { constructor(a, b) { super(); this.a = a; this.b = b; } } 67 | 68 | const commandHandlerMiddleware = new CommandHandlerMiddleware(); 69 | 70 | const result = commandHandlerMiddleware.execute(new SumCommand(1, 2)); 71 | 72 | expect(result).to.be.equal(null); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/handler/CommandNameExtractor/ClassNameExtractor.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import Command from '../../../src/Command'; 4 | import ClassNameExtractor from '../../../src/handler/CommandNameExtractor/ClassNameExtractor'; 5 | 6 | describe('Testing ClassNameExtractor', () => { 7 | it('Extract name from command', () => { 8 | const nameExtractor = new ClassNameExtractor(); 9 | class FooCommand extends Command {} 10 | const commandName = nameExtractor.extractName(new FooCommand()); 11 | expect(commandName).to.be.equal('FooCommand'); 12 | }); 13 | 14 | it('Invalid Command Object', () => { 15 | const nameExtractor = new ClassNameExtractor(); 16 | const fakeCommand = null; 17 | expect(() => nameExtractor.extractName(fakeCommand)).to.throw(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/handler/CommandNameExtractor/CommandNameExtractor.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import CommandNameExtractor from '../../../src/handler/CommandNameExtractor/CommandNameExtractor'; 4 | 5 | describe('Testing Abstract CommandNameExtractor', () => { 6 | it('Abstract instance thrown an error', () => { 7 | const nameExtractor = new CommandNameExtractor(); 8 | expect(nameExtractor.extractName).to.throw(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/handler/Locator/HandlerLocator.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import HandlerLocator from '../../../src/handler/Locator/HandlerLocator'; 4 | 5 | describe('Testing Abstract HandlerLocator', () => { 6 | it('Abstract instance thrown an error', () => { 7 | const locator = new HandlerLocator(); 8 | expect(locator.getHandlerForCommand).to.throw(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/handler/Locator/InMemoryLocator.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import { isObject } from 'lodash'; 4 | import Command from '../../../src/Command'; 5 | import InMemoryLocator from '../../../src/handler/Locator/InMemoryLocator'; 6 | 7 | describe('Testing InMemoryLocator', () => { 8 | it('Check handler function for command', () => { 9 | const locator = new InMemoryLocator({ FooHandler: () => {} }); 10 | const handler = locator.getHandlerForCommand('FooCommand'); 11 | 12 | expect(isObject(handler)).to.be.true; 13 | }); 14 | 15 | it('Check handler function for command with class', () => { 16 | class FooHandler {}; 17 | const locator = new InMemoryLocator({ FooHandler }); 18 | const handler = locator.getHandlerForCommand('FooCommand'); 19 | 20 | expect(isObject(handler)).to.be.true; 21 | expect(handler instanceof FooHandler).to.be.true; 22 | }); 23 | 24 | it('Invalid locator for non-valid command', () => { 25 | const locator = new InMemoryLocator({}); 26 | expect(() => locator.getHandlerForCommand(null)).to.throw(); 27 | }); 28 | 29 | it('Missing locator for command', () => { 30 | const locator = new InMemoryLocator({}); 31 | expect(() => locator.getHandlerForCommand('BarCommand')).to.throw(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/handler/Locator/NamespaceHandlerLocator.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickjth/simple-command-bus/1847b2cb5b6b31e2ae7d7baf9982e97be15c6e1c/test/handler/Locator/NamespaceHandlerLocator.js -------------------------------------------------------------------------------- /test/handler/MethodNameInflector/HandleInflector.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import Command from '../../../src/Command'; 4 | import HandleInflector from '../../../src/handler/MethodNameInflector/HandleInflector'; 5 | 6 | describe('Testing HandleInflector', () => { 7 | it('Testing valid handle method', () => { 8 | const inflector = new HandleInflector(); 9 | class FooCommand extends Command {} 10 | class FooHandler { handle(command) {} } 11 | const handleMethod = inflector.inflect(new FooCommand(), new FooHandler()); 12 | expect(handleMethod).to.be.equal('handle'); 13 | }); 14 | 15 | it('Handler without handle method', () => { 16 | const inflector = new HandleInflector(); 17 | const fakeCommand = null; 18 | class FooHandler { } 19 | expect(() => inflector.inflect(null, new FooHandler())).to.throw(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/handler/MethodNameInflector/MethodNameInflector.js: -------------------------------------------------------------------------------- 1 | /* global it, describe */ 2 | import { expect } from 'chai'; 3 | import MethodNameInflector from '../../../src/handler/MethodNameInflector/MethodNameInflector'; 4 | 5 | describe('Testing Abstract MethodNameInflector', () => { 6 | it('Abstract instance thrown an error', () => { 7 | const inflector = new MethodNameInflector(); 8 | expect(() => inflector.inflect(null)).to.throw(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/plugins/LoggerMiddleware.js: -------------------------------------------------------------------------------- 1 | /* global it, beforeEach, describe */ 2 | import { expect } from 'chai'; 3 | import Command from '../../src/Command'; 4 | import LoggerMiddleware from '../../src/plugins/LoggerMiddleware'; 5 | 6 | 7 | describe('Testing CommandHandlerMiddleware', function() { 8 | it('Testing constructor', function() { 9 | const loggerMiddleware = new LoggerMiddleware(console); 10 | 11 | expect(loggerMiddleware instanceof LoggerMiddleware).to.be.true; 12 | }); 13 | 14 | it('Testing execute', function() { 15 | const logger = { log: () => {} } 16 | const loggerMiddleware = new LoggerMiddleware(console); 17 | const command = { command: 'Command' }; 18 | const next = () => 'result'; 19 | const result = loggerMiddleware.execute(command, next); 20 | expect(result).to.be.equal('result'); 21 | }); 22 | }); 23 | --------------------------------------------------------------------------------