├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── collaborators.md ├── example ├── service-a.js └── service-b.js ├── lerna.json ├── package.json ├── packages ├── mu-balance │ ├── README.md │ ├── index.js │ └── package.json ├── mu-dns │ ├── README.md │ ├── index.js │ ├── package.json │ └── test │ │ ├── dns.errors.test.js │ │ ├── dns.redis.test.js │ │ ├── dns.test.js │ │ └── support │ │ └── dns.stub.js ├── mu-error │ ├── .npmignore │ ├── .travis.yml │ ├── .zuul.yml │ ├── LICENSE │ ├── README.md │ ├── index.js │ ├── package.json │ └── test │ │ └── index.js ├── mu-http │ ├── README.md │ ├── driver.js │ ├── index.js │ └── package.json ├── mu-local │ ├── README.md │ ├── driver.js │ ├── index.js │ └── package.json ├── mu-redis │ ├── README.md │ ├── driver.js │ ├── index.js │ └── package.json ├── mu-router │ ├── README.md │ ├── index.js │ ├── package.json │ └── test │ │ └── index.js ├── mu-tcp │ ├── README.md │ ├── driver.js │ ├── index.js │ └── package.json ├── mu-tee │ ├── README.md │ ├── index.js │ └── package.json ├── mu-transport │ ├── README.md │ ├── index.js │ ├── package.json │ └── test │ │ └── index.js └── mu │ ├── README.md │ ├── adapters │ ├── balancer.js │ └── tee.js │ ├── drivers │ ├── func.js │ ├── redis.js │ └── tcp.js │ ├── index.js │ └── package.json └── test ├── adapters ├── balancer.test.js └── tee.test.js ├── core ├── basic.test.js ├── legacy.test.js └── transport.test.js ├── drivers ├── http-driver │ ├── 500error.test.js │ ├── error.test.js │ ├── http.test.js │ ├── multi-response.test.js │ ├── routing-errors.test.js │ ├── service-chain.test.js │ ├── service-routing.test.js │ └── zero-response.test.js ├── local-driver │ ├── force-driver-error.test.js │ └── local.test.js ├── redis-driver │ └── redis.test.js └── tcp-driver │ ├── error.test.js │ ├── multi-response,test.js │ ├── routing-errors.test.js │ ├── service-chain.test.js │ ├── service-routing.test.js │ ├── tcp.test.js │ └── zero-response.test.js └── system ├── consumer ├── consumer.js └── responseCountConsumer.js ├── errorService ├── main.js └── service.js ├── multiResponseService └── service.js ├── service1 └── service.js ├── service2 └── service.js ├── service3 └── service.js └── zeroResponseService └── service.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | coverage 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | pids 12 | logs 13 | results 14 | node_modules 15 | npm-debug.log 16 | mochahelper.js 17 | .idea/ 18 | .settings/ 19 | dist 20 | .tmp 21 | .sass-cache 22 | app/bower_components 23 | options.mine.js 24 | db/ 25 | data/ 26 | .nyc_output/ 27 | pids 28 | *.seed 29 | lib-cov 30 | coverage 31 | .grunt 32 | .lock-wscript 33 | build/Release 34 | .npm 35 | .node_repl_history 36 | .__browserify_string_empty.js 37 | profile-* 38 | .nyc_output/ 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | 4 | node_js: 5 | - '4' 6 | - '6' 7 | - 'node' 8 | 9 | script: 10 | - npm run ci 11 | 12 | after_script: 13 | - npm run coveralls 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Mu Collaborators (see collaborators.md) 4 | 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mu 2 | 3 | [![npm][npm-badge]][npm-url] 4 | [![travis][travis-badge]][travis-url] 5 | [![coveralls][coveralls-badge]][coveralls-url] 6 | 7 | - __Sponsor:__ [nearForm][sponsor] 8 | - __Status:__ Experimental 9 | 10 | Mu is a message based router for building distributed systems. It allows messages to be routed and 11 | handled across various transports. Mu is aggressively light weight to ensure ease of use and speed 12 | of execution. 13 | 14 | * [Install](#install) 15 | * [API](#api) 16 | * [Development](#development) 17 | * [License](#license) 18 | 19 | 20 | ## Install 21 | To install mu, simply use npm, 22 | 23 | ```sh 24 | $ npm install mu 25 | ``` 26 | 27 | ## Example 28 | 29 | `service-a.js`: 30 | 31 | ```js 32 | const mu = require('mu')({dev: process.NODE_ENV !== 'production'}) 33 | const tcp = require('mu-tcp') 34 | 35 | // define routing: 36 | 37 | mu.inbound({role: 'some'}, tcp.server({port: 3000, host: '127.0.0.1'})) 38 | 39 | // define patterns: 40 | 41 | mu.define({role: 'some', cmd: 'thing'}, function (args, cb) { 42 | if (!args.pattern.user) { 43 | return cb(mu.error('no user found!')) 44 | } 45 | cb(null, {some: 'data'}) 46 | }) 47 | ``` 48 | 49 | `service-b.js`: 50 | 51 | ```js 52 | const mu = require('mu')({dev: process.NODE_ENV !== 'production'}) 53 | const tcp = require('mu-tcp') 54 | 55 | // define routing: 56 | 57 | mu.outbound({role: 'some'}, tcp.client({port: 3000, host: '127.0.0.1'})) 58 | 59 | // define patterns: 60 | 61 | mu.dispatch({role: 'some', cmd: 'thing', user: 'me :)'}, function (err, result) { 62 | if (err) { 63 | return console.error(err) 64 | } 65 | console.log(result) 66 | }) 67 | ``` 68 | 69 | ## API 70 | 71 | * [Core][mu-api] 72 | * [Error handling][mu-error-api] 73 | * Transports 74 | * [`mu-local`][mu-local-api] 75 | * [`mu-tcp`][mu-tcp-api] 76 | * [`mu-http`][mu-http-api] **(PLACEHOLDER - TODO)** 77 | * [`mu-redis`][mu-redis-api] 78 | * Adapters 79 | * [`mu-balance`][mu-balance-api] 80 | * [`mu-tee`][mu-tee-api] 81 | * Internals 82 | * [`mu-router`][mu-router-api] 83 | * [`mu-transport`][mu-transport-api] 84 | 85 | ## Packages 86 | 87 | The `mu` repo is managed as a monorepo, composed of multiple npm packages. 88 | 89 | | Package | Version | Dependencies | 90 | |--------|-------|------------| 91 | | [`mu`][] | [![npm](https://img.shields.io/npm/v/mu.svg?maxAge=2592000)](https://www.npmjs.com/package/mu) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu)](https://david-dm.org/apparatus/mu?path=packages/mu) | 92 | | [`mu-error`][] | [![npm](https://img.shields.io/npm/v/mu-error.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-error) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-error)](https://david-dm.org/apparatus/mu?path=packages/mu-error) | 93 | | [`mu-local`][] | [![npm](https://img.shields.io/npm/v/mu-local.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-local) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-local)](https://david-dm.org/apparatus/mu?path=packages/mu-local) | 94 | | [`mu-tcp`][] | [![npm](https://img.shields.io/npm/v/mu-tcp.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-tcp) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-tcp)](https://david-dm.org/apparatus/mu?path=packages/mu-tcp) | 95 | | [`mu-http`][] | N/A | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-http)](https://david-dm.org/apparatus/mu?path=packages/mu-http) | 96 | | [`mu-redis`][] | [![npm](https://img.shields.io/npm/v/mu-redis.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-redis) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-redis)](https://david-dm.org/apparatus/mu?path=packages/mu-redis) | 97 | | [`mu-balance`][] | [![npm](https://img.shields.io/npm/v/mu-balance.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-balance) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-balance)](https://david-dm.org/apparatus/mu?path=packages/mu-balance) | 98 | | [`mu-tee`][] | [![npm](https://img.shields.io/npm/v/mu-tee.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-tee) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-tee)](https://david-dm.org/apparatus/mu?path=packages/mu-tee) | 99 | | [`mu-router`][] | [![npm](https://img.shields.io/npm/v/mu-router.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-router) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-router)](https://david-dm.org/apparatus/mu?path=packages/mu-router) | 100 | | [`mu-transport`][] | [![npm](https://img.shields.io/npm/v/mu-transport.svg?maxAge=2592000)](https://www.npmjs.com/package/mu-transport) | [![Dependency Status](https://david-dm.org/apparatus/mu.svg?path=packages/mu-transport)](https://david-dm.org/apparatus/mu?path=packages/mu-transport) | 101 | 102 | 103 | ## Development 104 | 105 | The `mu` repository is Monorepo, managed with [lerna](http://lernajs.io) 106 | 107 | Each folder in packages is a distinct module, separately published to npm. 108 | 109 | But general admin such as tests, linting, coverage, and build pipeline is managed at this repositories top level. 110 | 111 | ### Getting Started 112 | 113 | Clone: 114 | 115 | ```sh 116 | $ git clone https://github.com/apparatus/mu 117 | ``` 118 | 119 | Setup: 120 | 121 | ```sh 122 | $ npm run setup 123 | ``` 124 | 125 | This runs the [lerna bootstrap](https://lernajs.io/#command-bootstrap) command, which installs all sub-deps and links common packages together. 126 | 127 | Our dev environment is now set up. 128 | 129 | ### QA 130 | 131 | - `npm test` - to run tests. Tests may be written at the top level, and in individual packages, this command will ensure all tests are run. 132 | - `npm run lint` - lint the code 133 | - `npm run coverage` - run and open a coverage report. 134 | - `npm run check` - test and lint (used as a precommit hook) 135 | 136 | ### Releasing 137 | 138 | ```sh 139 | npm run release 140 | ``` 141 | 142 | This runs the [lerna publish](https://lernajs.io/#command-publish) command, which discovers packages that have changes, prompts for a version, then publishes each to npm, and creates a git release for the Github repository. 143 | 144 | 145 | ## Contributors 146 | 147 | ### Peter Elger 148 | 149 | 150 | 151 | 152 | 153 | ### David Mark Clements 154 | 155 | 156 | 157 | 158 | 159 | ### Dean McDonnell 160 | 161 | 162 | 163 | 164 | 165 | ### Matteo Collina 166 | 167 | 168 | 169 | 170 | 171 | 172 | ## License 173 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 174 | 175 | [`mu`]: packages/mu 176 | [`mu-error`]: packages/mu-error 177 | [`mu-local`]: packages/mu-local 178 | [`mu-tcp`]: packages/mu-tcp 179 | [`mu-http`]: packages/mu-http 180 | [`mu-redis`]: packages/mu-redis 181 | [`mu-balance`]: packages/mu-balance 182 | [`mu-tee`]: packages/mu-tee 183 | [`mu-router`]: packages/mu-router 184 | [`mu-transport`]: packages/mu-transport#api 185 | [mu-api]: packages/mu 186 | [mu-error-api]: packages/mu-error#api 187 | [mu-local-api]: packages/mu-local#api 188 | [mu-tcp-api]: packages/mu-tcp#api 189 | [mu-http-api]: packages/mu-http#api 190 | [mu-redis-api]: packages/mu-redis#api 191 | [mu-balance-api]: packages/mu-balance#api 192 | [mu-tee-api]: packages/mu-tee#api 193 | [mu-router-api]: packages/mu-router#api 194 | [mu-transport-api]: packages/mu-transport#api 195 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 196 | [travis-url]: https://travis-ci.org/apparatus/mu 197 | [npm-badge]: https://badge.fury.io/js/mu.svg 198 | [npm-url]: https://npmjs.org/package/mu 199 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 200 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 201 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 202 | [sponsor]: http://nearform.com 203 | [MIT]: ./LICENSE 204 | -------------------------------------------------------------------------------- /collaborators.md: -------------------------------------------------------------------------------- 1 | Peter Elger 2 | David Mark Clements 3 | Dead McDonnell 4 | Matteo Collina 5 | -------------------------------------------------------------------------------- /example/service-a.js: -------------------------------------------------------------------------------- 1 | const mu = require('../packages/mu')({dev: process.NODE_ENV !== 'production'}) 2 | const tcp = require('../packages/mu-tcp') 3 | 4 | // define routing: 5 | 6 | mu.inbound({role: 'some'}, tcp.server({port: 3000, host: '127.0.0.1'})) 7 | 8 | // define patterns: 9 | 10 | mu.define({role: 'some', cmd: 'thing'}, function (args, cb) { 11 | if (!args.pattern.user) { 12 | return cb(mu.error('no user found!')) 13 | } 14 | cb(null, {some: 'data'}) 15 | }) 16 | -------------------------------------------------------------------------------- /example/service-b.js: -------------------------------------------------------------------------------- 1 | const mu = require('../packages/mu')({dev: process.NODE_ENV !== 'production'}) 2 | const tcp = require('../packages/mu-tcp') 3 | 4 | // define routing: 5 | 6 | mu.outbound({role: 'some'}, tcp.client({port: 3000, host: '127.0.0.1'})) 7 | 8 | // define patterns: 9 | 10 | mu.dispatch({role: 'some', cmd: 'thing', user: 'me :)'}, function (err, result) { 11 | if (err) { 12 | return console.error(err) 13 | } 14 | console.log(result) 15 | }) 16 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.0.0-beta.30", 3 | "version": "independent", 4 | "changelog": { 5 | "repo": "apparatus/mu", 6 | "labels": { 7 | "tag: breaking": ":boom: Breaking Change", 8 | "tag: feature": ":rocket: New Feature", 9 | "tag: fix": ":bug: Bug Fix", 10 | "tag: docs": ":memo: Documentation", 11 | "tag: internal": ":house: Internal" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MIT", 4 | "author": "Peter Elger (https://github.com/pelger)", 5 | "contributors": [ 6 | "David Mark Clements (https://github.com/davidmarkclements)", 7 | "Dean McDonnell (https://github.com/mcdonnelldean)", 8 | "Matteo Collina (https://github.com/mcollina)" 9 | ], 10 | "keywords": [ 11 | "microservices", 12 | "messaging", 13 | "router", 14 | "distributed" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/apparatus/mu.git" 19 | }, 20 | "main": "./core/core.js", 21 | "engines": { 22 | "node": ">=4.0.0" 23 | }, 24 | "scripts": { 25 | "setup": "npm i && lerna bootstrap", 26 | "clean": "lerna clean", 27 | "release": "lerna publish -i", 28 | "lint": "echo 'linting...' && spacey-standard | snazzy", 29 | "test": "tap 'test/**/*.test.js' 'packages/*/test/*.js' --branches=90 --statements=90 --functions=90 --lines=90 2>/dev/null", 30 | "check": "npm run test && npm run lint", 31 | "ci": "npm run setup && npm run check", 32 | "coverage": "npm run test -- --cov --coverage-report=html", 33 | "coveralls": "npm run test -- --cov --coverage-report=text-lcov | coveralls", 34 | "deps": "lerna publish --skip-npm --skip-git", 35 | "release": "lerna publish", 36 | "beta": "lerna publish --npm-tag=beta" 37 | }, 38 | "nyc": { 39 | "exclude": [ 40 | "**/test/**/*.js" 41 | ] 42 | }, 43 | "devDependencies": { 44 | "coveralls": "^2.11.14", 45 | "fakeredis": "^1.0.3", 46 | "lerna": "2.0.0-beta.30", 47 | "pre-commit": "^1.1.3", 48 | "proxyquire": "^1.7.10", 49 | "snazzy": "^5.0.0", 50 | "spacey-standard": "^3.0.0", 51 | "tap": "^7.1.2" 52 | }, 53 | "pre-commit": [ 54 | "check" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /packages/mu-balance/README.md: -------------------------------------------------------------------------------- 1 | # mu-balance 2 | 3 | The official load balance transport adapter for mu that distributes requests round robin to each supplied transport 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-balance` is a load balance transport adapter that distributes requests round robin to each supplied transport. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-balance 27 | ``` 28 | 29 | ## API 30 | To do.. 31 | 32 | ## License 33 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 34 | 35 | 36 | [mu]: https://github.com/apparatus/mu 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-balance 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu-balance/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var assert = require('assert') 18 | var uuid = require('uuid') 19 | 20 | /** 21 | * Load balance transport adapter. 22 | * distributes requests round robin to each supplied transport 23 | */ 24 | module.exports = function balance (transports) { 25 | return function adapter (mu, opts) { 26 | assert(opts) 27 | 28 | var direction = opts.direction 29 | var muid = opts.id || uuid() 30 | var index = 0 31 | 32 | transports = transports.map(function (t) { 33 | return t(mu, {direction: direction, id: muid}) 34 | }) 35 | 36 | return { 37 | direction: transports[0].direction, 38 | muid: muid, 39 | tf: tf, 40 | tearDown: tearDown, 41 | type: 'transport' 42 | } 43 | 44 | function tf (message, cb) { 45 | transports[index].tf(message, cb) 46 | index++ 47 | if (index >= transports.length) { 48 | index = 0 49 | } 50 | } 51 | 52 | function tearDown (cb) { 53 | var count = 0 54 | transports.forEach(function (transport) { 55 | transport.tearDown(function () { 56 | ++count 57 | if (count === transports.length) { 58 | cb && cb() 59 | } 60 | }) 61 | }) 62 | } 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /packages/mu-balance/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-balance", 3 | "version": "1.0.5", 4 | "description": "The official load balance transport adapter for mu that distributes requests round robin to each supplied transport", 5 | "main": "index.js", 6 | "keywords": [ 7 | "balancer", 8 | "mu", 9 | "adapter", 10 | "microservices", 11 | "messaging", 12 | "router", 13 | "distributed" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/apparatus/mu.git" 18 | }, 19 | "author": "Peter Elger (https://github.com/pelger)", 20 | "contributors": [ 21 | "David Mark Clements (https://github.com/davidmarkclements)", 22 | "Dean McDonnell (https://github.com/mcdonnelldean)", 23 | "Matteo Collina (https://github.com/mcollina)" 24 | ], 25 | "license": "MIT", 26 | "dependencies": { 27 | "uuid": "^3.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/mu-dns/README.md: -------------------------------------------------------------------------------- 1 | # mu-transport 2 | 3 | The official dns service discovery adapter for mu 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-dns` is the dns service discovery adapter for [mu][`mu`]. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-dns 27 | ``` 28 | 29 | ## Purpose 30 | The mu-dns adapter performs DNS lookups either directly against a nominated DNS server or using the system resolver mechanism. It is intended for use in container based deployments for example with Kubernetes or with the `fuge` development tool. 31 | 32 | ## Operation 33 | 34 | ### Lookup sequence 35 | Mu-dns resolves services as follows: 36 | 37 | * Perform an SRV query to determine service port number and canonical name 38 | * Then perform an A query against the canonical name to determine one or more ip addresses 39 | 40 | ### mode 41 | Mu-dns operates in one of two modes, direct or system depending on the environment configuration. 42 | 43 | * if `DNS_HOST` variable is present in the environment then a direct lookup is performed in this case 44 | * Use `DNS_HOST` and `DNS_PORT` to connect directly to a DNS server. If `DNS_PORT` is not set default to 53053 as the port number 45 | * if `DNS_HOST` is not present then use the system based resolver for queries 46 | 47 | ### Query formation 48 | Mu-dns forms SRV queries as follows: 49 | 50 | ``` 51 | _._... 52 | ``` 53 | 54 | The query is constructed as follows: 55 | 56 | * `` must be supplied 57 | * `` may be supplied, defaults to '_tcp' 58 | * `` must be supplied 59 | * `` may be supplied, otherwise the `DNS_NAMESPACE` variable is used if supplied, otherwise defaults to 'default' 60 | * `` may be supplied, otherwise the `DNS_SUFFIX` variable is used if supplied, otherwise defaults to 'svc.cluster.local' 61 | 62 | 63 | ## Examples 64 | 65 | ```javascript 66 | var mu = require('mu')() 67 | var tcp = require('mu-tcp') 68 | var dns = require('mu-dns') 69 | 70 | mu.outbound({role: 'basic'}, dns(tcp, {name: 'my_service', portName: '_main'})) 71 | ``` 72 | 73 | In the above example service name and portName are supplied, this will result in SRV query: '_main._tcp.my_service.default.svc.cluster.local' 74 | 75 | 76 | ## License 77 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 78 | 79 | 80 | [mu]: https://github.com/apparatus/mu 81 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 82 | [travis-url]: https://travis-ci.org/apparatus/mu 83 | [npm-badge]: https://badge.fury.io/js/mu.svg 84 | [npm-url]: https://npmjs.org/package/mu 85 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 86 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 87 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 88 | [david-badge]: https://david-dm.org/apparatus/mu.svg 89 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-transport 90 | [sponsor]: http://nearform.com 91 | [MIT]: ./LICENSE 92 | -------------------------------------------------------------------------------- /packages/mu-dns/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var assert = require('assert') 18 | var uuid = require('uuid') 19 | var _ = require('lodash') 20 | var concordant = require('concordant') 21 | var mue = require('mu-error')() 22 | 23 | 24 | /** 25 | * dns based service discovery for mu 26 | */ 27 | module.exports = function muDns (transport, service) { 28 | return function adapter (mu, opts) { 29 | assert(opts) 30 | 31 | var index = 0 32 | var direction = opts.direction 33 | var muid = opts.id || uuid() 34 | var lastLookup = 0 35 | var namespace 36 | var suffix 37 | var query 38 | var protocol 39 | var endpoints = [] 40 | var lookupInterval = process.env.DNS_LOOKUP_INTERVAL || 60 41 | var extra 42 | var conc = concordant() 43 | 44 | assert(transport) 45 | assert(service) 46 | assert(service.portName && service.name) 47 | 48 | 49 | function init () { 50 | namespace = process.env.DNS_NAMESPACE || 'default' 51 | if (service.namespace) { namespace = service.namespace } 52 | 53 | suffix = process.env.DNS_SUFFIX || 'svc.cluster.local' 54 | if (service.suffix) { suffix = service.suffix } 55 | 56 | protocol = '_tcp' 57 | if (service.protocol) { protocol = service.protocol } 58 | 59 | query = service.portName + '.' + protocol + '.' + service.name 60 | if (namespace.length > 0) { 61 | query += '.' + namespace 62 | } 63 | query += '.' + suffix 64 | 65 | extra = _.omit(service, ['portName', 'name']) 66 | 67 | if (direction === 'inbound') { 68 | lookup(query, function () {}) 69 | } 70 | } 71 | 72 | 73 | 74 | function createTransports (endpoints, cb) { 75 | endpoints.forEach(function (endpoint) { 76 | if (!endpoint.created) { 77 | if (direction === 'outbound') { 78 | endpoint.transport = transport.client(_.merge({port: endpoint.port, host: endpoint.host}, extra))(mu, {direction: direction, id: muid}) 79 | } else { 80 | endpoint.transport = transport.server(_.merge({port: endpoint.port, host: endpoint.host}, extra))(mu, {direction: direction, id: muid}) 81 | } 82 | endpoint.created = true 83 | } 84 | }) 85 | cb() 86 | } 87 | 88 | 89 | 90 | function lookupRequired () { 91 | if (Date.now() - lastLookup > lookupInterval * 1000) { 92 | lastLookup = Date.now() 93 | return true 94 | } 95 | return false 96 | } 97 | 98 | 99 | 100 | function lookup (query, cb) { 101 | if (lookupRequired()) { 102 | conc.dns.resolve(query, function (err, results) { 103 | if (err) { return cb(err) } 104 | _.each(results, function (result) { 105 | if (!_.find(endpoints, function (endpoint) { return endpoint.host === result.host && endpoint.port === result.port })) { 106 | endpoints.push({port: result.port, host: result.host}) 107 | } 108 | }) 109 | createTransports(endpoints, cb) 110 | }) 111 | } else { 112 | cb() 113 | } 114 | } 115 | 116 | 117 | 118 | function tf (message, cb) { 119 | lookup(query, function (err) { 120 | if (err) { return cb(mue.transport(err)) } 121 | 122 | endpoints[index] && endpoints[index].transport.tf(message, cb) 123 | index++ 124 | if (index >= endpoints.length) { 125 | index = 0 126 | } 127 | }) 128 | } 129 | 130 | 131 | 132 | function tearDown (cb) { 133 | var count = 0 134 | endpoints.forEach(function (endpoint) { 135 | endpoint.transport.tearDown(function () { 136 | ++count 137 | if (count === endpoints.length) { 138 | cb && cb() 139 | } 140 | }) 141 | }) 142 | } 143 | 144 | 145 | 146 | init() 147 | 148 | return { 149 | direction: direction, 150 | muid: muid, 151 | tf: tf, 152 | tearDown: tearDown, 153 | type: 'transport' 154 | } 155 | } 156 | } 157 | 158 | -------------------------------------------------------------------------------- /packages/mu-dns/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-dns", 3 | "version": "1.0.5", 4 | "description": "The official dns discovery adapter for mu", 5 | "main": "index.js", 6 | "keywords": [ 7 | "mu", 8 | "transport", 9 | "microservices", 10 | "messaging", 11 | "router", 12 | "distributed", 13 | "dns", 14 | "service", 15 | "discovery" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/apparatus/mu.git" 20 | }, 21 | "author": "Peter Elger (https://github.com/pelger)", 22 | "contributors": [], 23 | "license": "MIT", 24 | "dependencies": { 25 | "async": "^2.1.4", 26 | "concordant": "^0.2.1", 27 | "dns-socket": "^1.4.2", 28 | "mu-error": "^1.4.5", 29 | "uuid": "^3.0.1" 30 | }, 31 | "devDependencies": { 32 | "abstract-logging": "^1.0.0", 33 | "fuge-dns": "^0.2.0", 34 | "mu": "^2.1.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/mu-dns/test/dns.errors.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var Mu = require('../../mu') 19 | var tcp = require('../../mu-tcp') 20 | var proxyquire = require('proxyquire') 21 | var dnsMock = require('./support/dns.stub.js')() 22 | var service1 = require('../../../test/system/service1/service') 23 | proxyquire('concordant/dnsResolver', {dns: dnsMock.systemStub, 'dns-socket': dnsMock.dnsErrorSocketStub}) 24 | var dns = require('../index') 25 | 26 | function initS1 (cb) { 27 | service1(function (s1) { 28 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 29 | cb(s1) 30 | }) 31 | } 32 | 33 | 34 | test('test error lookup with system dns', function (t) { 35 | t.plan(2) 36 | var mu = Mu() 37 | 38 | process.env.DNS_LOOKUP_INTERVAL = 60 39 | 40 | initS1(function (s1) { 41 | dnsMock.setErrorSRV(true) 42 | dnsMock.setErrorA(false) 43 | 44 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 45 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 46 | setTimeout(function () { 47 | t.notequal(null, err, 'test error condition on bad lookup') 48 | t.equal(err.output.mu.message, 'ENODATA', 'test that error is no data from dns') 49 | mu.tearDown() 50 | s1.tearDown() 51 | }, 100) 52 | }) 53 | }) 54 | }) 55 | 56 | 57 | test('test error lookup with system dns', function (t) { 58 | t.plan(2) 59 | var mu = Mu() 60 | 61 | process.env.DNS_NAMESPACE = 'testns' 62 | process.env.DNS_LOOKUP_INTERVAL = 60 63 | 64 | initS1(function (s1) { 65 | dnsMock.setErrorSRV(true) 66 | dnsMock.setErrorA(false) 67 | 68 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 69 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 70 | setTimeout(function () { 71 | t.notequal(null, err, 'test error condition on bad lookup') 72 | t.equal(err.output.mu.message, 'ENODATA', 'test that error is no data from dns') 73 | mu.tearDown() 74 | s1.tearDown() 75 | }, 100) 76 | }) 77 | }) 78 | }) 79 | 80 | 81 | test('test error lookup with system dns', function (t) { 82 | t.plan(2) 83 | var mu = Mu() 84 | 85 | process.env.DNS_NAMESPACE = 'testns' 86 | process.env.DNS_LOOKUP_INTERVAL = 60 87 | 88 | initS1(function (s1) { 89 | dnsMock.setErrorSRV(false) 90 | dnsMock.setErrorA(true) 91 | 92 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 93 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 94 | setTimeout(function () { 95 | t.notequal(null, err, 'test error condition on bad lookup') 96 | t.equal(err.output.mu.message, 'force error A', 'test that error is no data from dns') 97 | mu.tearDown() 98 | s1.tearDown() 99 | }, 100) 100 | }) 101 | }) 102 | }) 103 | 104 | 105 | 106 | test('test error on lookup with development dns SRV', function (t) { 107 | t.plan(2) 108 | var mu = Mu() 109 | 110 | process.env.DNS_NAMESPACE = 'testns' 111 | process.env.DNS_PORT = 53053 112 | process.env.DNS_HOST = '127.0.0.1' 113 | 114 | initS1(function (s1) { 115 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 116 | 117 | dnsMock.setErrorSRV(true) 118 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 119 | setTimeout(function () { 120 | t.notequal(null, err, 'test error condition on bad lookup') 121 | t.equal(err.output.mu.message, 'force error on SRV', 'test that error is no data from dns') 122 | mu.tearDown() 123 | s1.tearDown() 124 | }, 100) 125 | }) 126 | }) 127 | }) 128 | 129 | 130 | test('test error on lookup with development dns A', function (t) { 131 | t.plan(2) 132 | var mu = Mu() 133 | 134 | process.env.DNS_NAMESPACE = 'testns' 135 | process.env.DNS_PORT = 53053 136 | process.env.DNS_HOST = '127.0.0.1' 137 | 138 | initS1(function (s1) { 139 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 140 | 141 | dnsMock.setErrorSRV(false) 142 | dnsMock.setErrorA(true) 143 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 144 | setTimeout(function () { 145 | t.notequal(null, err, 'test error condition on bad lookup') 146 | t.equal(err.output.mu.message, 'force error on A', 'test that error is no transports available') 147 | mu.tearDown() 148 | s1.tearDown() 149 | }, 100) 150 | }) 151 | }) 152 | }) 153 | 154 | -------------------------------------------------------------------------------- /packages/mu-dns/test/dns.redis.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var proxyquire = require('proxyquire') 19 | var redisMock = require('fakeredis') 20 | var driver = proxyquire('../../mu-redis/driver', {redis: redisMock}) 21 | var rdis = proxyquire('../../mu-redis', {driver: driver}) 22 | var dnsMock = require('./support/dns.stub.js')() 23 | proxyquire('concordant/dnsResolver', {dns: dnsMock.systemStub}) 24 | var dns = require('../index') 25 | 26 | 27 | function init (cb) { 28 | require('../../../test/system/service1/service')(function (s1) { 29 | s1.inbound('*', dns(rdis, {portName: '_redis', name: 'redis', list: 's1'})) 30 | require('../../../test/system/service2/service')(function (s2) { 31 | s2.inbound('*', dns(rdis, {portName: '_redis', name: 'redis', list: 's2'})) 32 | cb(s1, s2) 33 | }) 34 | }) 35 | } 36 | 37 | 38 | 39 | test('consume services with redis transport test', function (t) { 40 | t.plan(1) 41 | 42 | process.env.DNS_NAMESPACE = 'testns' 43 | process.env.DNS_PORT = 53053 44 | process.env.DNS_HOST = '127.0.0.1' 45 | process.env.DNS_LOOKUP_INTERVAL = 60 46 | 47 | dnsMock.start(function () { 48 | setTimeout(function () { 49 | init(function (s1, s2) { 50 | var consumer = require('../../../test/system/consumer/consumer')() 51 | consumer.mu.outbound({role: 's1'}, dns(rdis, {portName: '_redis', name: 'redis', list: 's1'})) 52 | consumer.mu.outbound({role: 's2'}, dns(rdis, {portName: '_redis', name: 'redis', list: 's2'})) 53 | consumer.consume(function (err, result) { 54 | t.equal(err, null, 'check err is null') 55 | consumer.mu.tearDown() 56 | s1.tearDown() 57 | s2.tearDown() 58 | dnsMock.stop() 59 | }) 60 | }) 61 | }, 100) 62 | }) 63 | }) 64 | 65 | -------------------------------------------------------------------------------- /packages/mu-dns/test/dns.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | // var mu = require('../../mu')() 19 | var Mu = require('../../mu') 20 | var tcp = require('../../mu-tcp') 21 | var proxyquire = require('proxyquire') 22 | 23 | var dnsMock = require('./support/dns.stub.js')() 24 | var service1 = require('../../../test/system/service1/service') 25 | var service2 = require('../../../test/system/service2/service') 26 | 27 | proxyquire('concordant/dnsResolver', {dns: dnsMock.systemStub}) 28 | var dns = require('../index') 29 | 30 | 31 | function init (cb) { 32 | service1(function (s1) { 33 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 34 | service2(function (s2) { 35 | s2.inbound('*', tcp.server({port: 3002, host: '127.0.0.1'})) 36 | cb(s1, s2) 37 | }) 38 | }) 39 | } 40 | 41 | 42 | 43 | function initS1 (cb) { 44 | service1(function (s1) { 45 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 46 | cb(s1) 47 | }) 48 | } 49 | 50 | 51 | test('consume services with system dns lookup', function (t) { 52 | t.plan(3) 53 | var mu = Mu() 54 | 55 | process.env.DNS_NAMESPACE = 'testns' 56 | 57 | init(function (s1, s2) { 58 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 59 | mu.outbound({role: 's2'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service2'})) 60 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 61 | t.equal(null, err, 'test no error on lookup and consume service1') 62 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 63 | t.equal(null, err, 'test no error on lookup and consume service2') 64 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 65 | t.equal(null, err, 'test no error on lookup and consume service2') 66 | mu.tearDown() 67 | s1.tearDown() 68 | s2.tearDown() 69 | }) 70 | }) 71 | }) 72 | }) 73 | }) 74 | 75 | 76 | test('consume services with system dns lookup adjusting lookup interval', function (t) { 77 | t.plan(3) 78 | var mu = Mu() 79 | 80 | process.env.DNS_NAMESPACE = 'testns' 81 | process.env.DNS_LOOKUP_INTERVAL = 0 82 | 83 | setTimeout(function () { 84 | init(function (s1, s2) { 85 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 86 | mu.outbound({role: 's2'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service2', namespace: 'testns', suffix: 'svc.cluster.local'})) 87 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 88 | t.equal(null, err, 'test no error on lookup and consume service1') 89 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 90 | t.equal(null, err, 'test no error on lookup and consume service2') 91 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 92 | t.equal(null, err, 'test no error on lookup and consume service2') 93 | mu.tearDown() 94 | s1.tearDown() 95 | s2.tearDown() 96 | }) 97 | }) 98 | }) 99 | }) 100 | }, 100) 101 | }) 102 | 103 | 104 | test('test fail lookup with system dns', function (t) { 105 | t.plan(2) 106 | var mu = Mu() 107 | 108 | process.env.DNS_NAMESPACE = 'testns' 109 | process.env.DNS_LOOKUP_INTERVAL = 60 110 | 111 | setTimeout(function () { 112 | initS1(function (s1) { 113 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'wibble'})) 114 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 115 | setTimeout(function () { 116 | t.notequal(null, err, 'test error condition on bad lookup') 117 | t.equal(err.output.mu.message, 'ENODATA', 'test that error is no data from dns') 118 | mu.tearDown() 119 | s1.tearDown() 120 | }, 100) 121 | }) 122 | }) 123 | }, 100) 124 | }) 125 | 126 | 127 | 128 | test('consume services with development dns server', function (t) { 129 | t.plan(3) 130 | var mu = Mu() 131 | 132 | process.env.DNS_NAMESPACE = 'testns' 133 | process.env.DNS_PORT = 53053 134 | process.env.DNS_HOST = '127.0.0.1' 135 | process.env.DNS_LOOKUP_INTERVAL = 60 136 | 137 | setTimeout(function () { 138 | dnsMock.start(function () { 139 | init(function (s1, s2) { 140 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 141 | mu.outbound({role: 's2'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service2'})) 142 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 143 | t.equal(null, err, 'test no error on lookup and consume service1') 144 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 145 | t.equal(null, err, 'test no error on lookup and consume service2') 146 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 147 | t.equal(null, err, 'test no error on lookup and consume service2') 148 | mu.tearDown() 149 | s1.tearDown() 150 | s2.tearDown() 151 | dnsMock.stop() 152 | }) 153 | }) 154 | }) 155 | }) 156 | }) 157 | }, 100) 158 | }) 159 | 160 | 161 | 162 | test('consume services with development dns server adjusting lookup interval', function (t) { 163 | t.plan(3) 164 | var mu = Mu() 165 | 166 | process.env.DNS_NAMESPACE = 'testns' 167 | process.env.DNS_PORT = 53053 168 | process.env.DNS_HOST = '127.0.0.1' 169 | process.env.DNS_LOOKUP_INTERVAL = 0 170 | 171 | setTimeout(function () { 172 | dnsMock.start(function () { 173 | init(function (s1, s2) { 174 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service1'})) 175 | mu.outbound({role: 's2'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'service2'})) 176 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 177 | t.equal(null, err, 'test no error on lookup and consume service1') 178 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 179 | t.equal(null, err, 'test no error on lookup and consume service2') 180 | mu.dispatch({role: 's2', cmd: 'two', fish: 'cheese'}, function (err, result) { 181 | t.equal(null, err, 'test no error on lookup and consume service2') 182 | mu.tearDown() 183 | s1.tearDown() 184 | s2.tearDown() 185 | dnsMock.stop() 186 | }) 187 | }) 188 | }) 189 | }) 190 | }) 191 | }, 100) 192 | }) 193 | 194 | 195 | 196 | test('test fail lookup with development dns', function (t) { 197 | t.plan(2) 198 | var mu = Mu() 199 | 200 | process.env.DNS_NAMESPACE = 'testns' 201 | process.env.DNS_PORT = 53053 202 | process.env.DNS_HOST = '127.0.0.1' 203 | process.env.DNS_LOOKUP_INTERVAL = 60 204 | 205 | setTimeout(function () { 206 | dnsMock.start(function () { 207 | initS1(function (s1) { 208 | mu.outbound({role: 's1'}, dns(tcp, {portName: '_tcp', protocol: '_tcp', name: 'wibble'})) 209 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 210 | setTimeout(function () { 211 | t.notequal(null, err, 'test error condition on bad lookup') 212 | t.equal(err.output.mu.message, 'ENODATA', 'test that error is no data from dns') 213 | mu.tearDown() 214 | s1.tearDown() 215 | dnsMock.stop() 216 | }, 100) 217 | }) 218 | }) 219 | }) 220 | }, 100) 221 | }) 222 | 223 | -------------------------------------------------------------------------------- /packages/mu-dns/test/support/dns.stub.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var FugeDns = require('fuge-dns') 18 | var systemDns = require('dns') 19 | var zone = { 20 | 'A': { 21 | 'service1.testns.svc.cluster.local': { 22 | 'address': '127.0.0.1' 23 | }, 24 | 'service2.testns.svc.cluster.local': { 25 | 'address': '127.0.0.1' 26 | }, 27 | 'redis.testns.svc.cluster.local': { 28 | 'address': '127.0.0.1' 29 | } 30 | }, 31 | 'SRV': { 32 | '_tcp._tcp.service1.testns.svc.cluster.local': { 33 | 'cname': 'service1.testns.svc.cluster.local', 34 | 'port': '3001' 35 | }, 36 | '_tcp._tcp.service2.testns.svc.cluster.local': { 37 | 'cname': 'service2.testns.svc.cluster.local', 38 | 'port': '3002' 39 | }, 40 | '_redis._tcp.redis.testns.svc.cluster.local': { 41 | 'cname': 'redis.testns.svc.cluster.local', 42 | 'port': '6379' 43 | } 44 | } 45 | } 46 | 47 | 48 | 49 | module.exports = function () { 50 | var dns 51 | var errorA = false 52 | var errorSRV = false 53 | 54 | 55 | function start (cb) { 56 | dns = FugeDns({host: '127.0.0.1', port: 53053}) 57 | dns.addZone(zone) 58 | dns.start(cb) 59 | } 60 | 61 | 62 | 63 | function stop (cb) { 64 | dns.stop(cb) 65 | } 66 | 67 | 68 | var systemStub = { 69 | resolveSrv: function (query, cb) { 70 | if (errorSRV) { 71 | return cb(null, []) 72 | } 73 | if (zone.SRV[query]) { 74 | cb(null, [{name: zone.SRV[query].cname, port: zone.SRV[query].port}]) 75 | } else { 76 | cb(systemDns.NODATA) 77 | } 78 | }, 79 | resolve4: function (query, cb) { 80 | if (errorA) { 81 | return cb('force error A') 82 | } 83 | if (zone.A[query]) { 84 | cb(null, [{address: zone.A[query].address}]) 85 | } else { 86 | cb(systemDns.NODATA) 87 | } 88 | }, 89 | NODATA: systemDns.NODATA 90 | } 91 | 92 | 93 | 94 | function setErrorA (e) { errorA = e } 95 | function setErrorSRV (e) { errorSRV = e } 96 | 97 | var dnsErrorSocketStub = function () { 98 | 99 | function query (q, port, host, cb) { 100 | var resp = { id: 14978, 101 | type: 'response', 102 | flags: 0, 103 | questions: 104 | [ { name: '_tcp._tcp.service2.testns.svc.cluster.local', 105 | type: 'SRV', 106 | class: 1 } ], 107 | answers: 108 | [ { name: '_tcp._tcp.service2.testns.svc.cluster.local', 109 | type: 'SRV', 110 | class: 1, 111 | ttl: 5, 112 | flush: false, 113 | data: [Object] } ], 114 | authorities: [], 115 | additionals: [] } 116 | 117 | if (q.questions[0].type === 'SRV') { 118 | if (errorSRV) { 119 | return cb('force error on SRV') 120 | } else { 121 | cb(null, resp) 122 | } 123 | } 124 | 125 | if (q.questions[0].type === 'A') { 126 | if (errorA) { 127 | return cb('force error on A') 128 | } 129 | } 130 | } 131 | 132 | function destroy () { 133 | } 134 | 135 | return { 136 | query: query, 137 | destroy: destroy 138 | } 139 | } 140 | 141 | 142 | return { 143 | start: start, 144 | stop: stop, 145 | systemStub: systemStub, 146 | setErrorA: setErrorA, 147 | setErrorSRV: setErrorSRV, 148 | dnsErrorSocketStub: dnsErrorSocketStub 149 | } 150 | } 151 | 152 | -------------------------------------------------------------------------------- /packages/mu-error/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apparatus/mu/c084ec60b5e3a7c163bead9aedbb59eefa1ea787/packages/mu-error/.npmignore -------------------------------------------------------------------------------- /packages/mu-error/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - '4' 5 | - '5' 6 | - '6' 7 | script: 8 | - npm run ci -------------------------------------------------------------------------------- /packages/mu-error/.zuul.yml: -------------------------------------------------------------------------------- 1 | name: mu-error 2 | ui: tape -------------------------------------------------------------------------------- /packages/mu-error/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 David Mark Clements 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/mu-error/README.md: -------------------------------------------------------------------------------- 1 | # mu-error 2 | 3 | The official error handling library for mu 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-error` provides distributed error handling functionality to [mu][`mu`]. Of note, it ensures stack serialization, preserves remote stack traces and prevents exposing internal errors to production users. 16 | 17 | * [Overview](#install) 18 | * [Quick Start](#quick-start) 19 | * [API](#api) 20 | * [Features](#features) 21 | * [Install](#install) 22 | * [License](#license) 23 | 24 | ## Overview 25 | 26 | `mu-error` is exported on the `mu` object as `mu.error`. There is usually no need to install. 27 | 28 | `mu.error` creates decorated [`boom`](http://npm.im/boom) objects, which allows 29 | for compatibility with the Hapi `reply` function, without losing any value when 30 | integrating with other frameworks such as Express or Koa. 31 | 32 | While `boom` allows HTTP status codes of 400+, `mu-error` reserves the range 33 | of 1..99 for mu specific errors, creating an error with this a code this range 34 | will generate an HTTP status code of 500 by default and inject the mu error context into the `boom` error `output` object. 35 | 36 | ## Quick Start 37 | 38 | ### Example 39 | 40 | If used in a service context, simply call `mu.error` with a string. 41 | 42 | ```js 43 | const mu = require('mu')({dev: process.NODE_ENV !== 'production'}) 44 | 45 | mu.define({role: 'some': cmd: 'thing'}, function (args, cb) { 46 | if (!args.pattern.user) { 47 | return cb(mu.error('no user found!')) 48 | } 49 | cb(null, {some: 'data'}) 50 | }) 51 | ``` 52 | 53 | This will create an object like the following: 54 | 55 | ```js 56 | { Error: , 57 | data: null, 58 | isBoom: true, 59 | isServer: true, 60 | output: 61 | { statusCode: 500, 62 | payload: 63 | { statusCode: 500, 64 | error: 'Internal Server Error', 65 | message: 'An internal server error occurred' }, 66 | mu: 67 | { code: 1, 68 | error: 'general service error', 69 | message: 'no user found!' } }, 70 | headers: {}, 71 | mu: 72 | { code: 1, 73 | error: 'general service error', 74 | message: 'no user found!' } }, 75 | reformat: [Function], 76 | isMu: true } 77 | ``` 78 | 79 | This output assumes `NODE_ENV` is not 'production', when not in dev mode the `output.payload.mu` is not added, which means error specifics are hidden from production users. 80 | 81 | 82 | ## API 83 | 84 | ### `require('mu')({errors: MU_ERROR_OPTIONS})` 85 | 86 | `mu-error` is created automatically by `mu`, the `errors` property of the options object passed to `mu` is passed on to `mu-error` which has the following options: 87 | 88 | * `dev` (`false`) if set on the `errors` object it can override the top level `dev` option for mu - see [Dev Mode](#dev-mode) 89 | * `httpCode` (`500`) - see [Custom mu error HTTP status code](#custom-mu-error-http-status-code) 90 | * `serializeErrorProps` (`true`) - see [Serialize Error Props](#serialize-error-props) 91 | * `maxRemoteStacks` (`dev ? Infinity : 20`) see [Remote Errors](#remote-errors) 92 | 93 | ### `mu.error(code|message, message|data, data)` 94 | 95 | The main function for creating mu error objects. 96 | 97 | The first arguments may be a number between 1 and 99, or 400+, 98 | or a string (or undefined). 99 | 100 | As with boom, a `data` parameter can be passed to attach any useful state to the 101 | error context. 102 | 103 | **Alias**: `mu.error.create` 104 | 105 | ### `mu.error.wrap(error, muCode, statusCode, message)` 106 | 107 | Same concept as `boom.wrap`. 108 | 109 | Wraps an `Error` (or `boom`) object (included deserialized `boom` objects) with a mu error object, 110 | or adds mu context to a pre-existing `boom` object. 111 | 112 | ### `mu.error.wrapRemote(error, muCode, statusCode, message)` 113 | 114 | Intended to wraps a (usually deserialized `boom` schema) object (i.e. an error propagating across a transport), and keeps a live list of remote stacks as an array on `err.remoteStacks`. See [Remote Errors](#remote-errors). 115 | 116 | **Alias**: `mu.error.remote` 117 | 118 | ### `mu.error.makeMuError(muCode, httpStatusCode, message, data)` 119 | 120 | Directly create a mu error object, this method can be used to 121 | create a single mu error object with a custom http status code. 122 | 123 | ### `mu.error.extract(err)` 124 | 125 | Inverts the shape of the `boom` object so that the mu error context is at the top level, along with payload and data objects. 126 | 127 | For example: 128 | 129 | ```js 130 | console.log(mu.error.extract(mu.error('no user found!'))) 131 | ``` 132 | 133 | Would give 134 | 135 | ```js 136 | { code: 1, 137 | error: 'service error', 138 | message: 'no user found!', 139 | data: null, 140 | payload: 141 | { statusCode: 500, 142 | error: 'Internal Server Error', 143 | message: 'An internal server error occurred' } } 144 | ``` 145 | 146 | ### Mu Constants 147 | 148 | The `mu.error.ERRORS` object has the following constants 149 | 150 | ```js 151 | SERVICE: 1, 152 | FRAMEWORK: 2, 153 | TRANSPORT: 3, 154 | UNKNOWN: 99 155 | ``` 156 | 157 | ### Mu Codes 158 | 159 | The following codes (`mu.error.MU_CODES`) represent internal mu errors 160 | 161 | ```js 162 | 1: 'service error', 163 | 2: 'framework error', 164 | 3: 'transport error', 165 | 99: 'unknown' 166 | ``` 167 | 168 | In userland the only code currently of interest is the `service error`, the other codes are used in mu internally. 169 | 170 | ### `mu.error.service(message|Error, data)` 171 | 172 | Generate a service error. 173 | 174 | When passed a message, this is functionally equivalent to calling `mu.error` directly without a code. (`mu.error('message') === mu.error.service('message')`). 175 | 176 | When passed an `Error` (or `boom`) object it wraps the object with the correct mu context 177 | 178 | When passed an `Error` this is the equivalent of calling `mu.error.wrap` (`mu.error.wrap(Error('foo') === mu.error.service(Error('foo'))`) 179 | 180 | ### `mu.error.framework(message|Error, data)` 181 | 182 | Generate a framework error, used internally by mu 183 | 184 | When passed an `Error` (or `boom`) object it wraps the object with the correct mu context 185 | 186 | ### `mu.error.transport(message|Error, data)` 187 | 188 | Generate a transport error, used internally by mu 189 | 190 | When passed an `Error` (or `boom`) object it wraps the object with the correct mu context 191 | 192 | ## Features 193 | 194 | ### Remote Errors 195 | 196 | Errors that have been propagated from another service can be passed to `mu.error.wrapRemote`. This is for cases where the deserialized `stack` property relates to a stack in another process. Passing the error to `mu.error.wrapRemote` will place a new local stack on the `err.stack` property, and append the remote stack to a `err.remoteStacks` array, which contains the object of the form `{timestamp, stack}`. 197 | 198 | The `maxRemoteStacks` option can be used to set the maximum allowed stacks to retain in `err.remoteStacks`. This defaults to 30 when the `dev` option is false, or `Infinity` in `dev` mode. 199 | 200 | ### Generate Specific HTTP errors 201 | 202 | `mu.error` can be used in the same way as `boom` to create http errors 203 | 204 | ```js 205 | mu.define({role: 'some': cmd: 'thing'}, function (args, cb) { 206 | if (!args.pattern.user) { 207 | return cb(mu.error(401, 'no user found')) 208 | } 209 | cb(null, {some: 'data'}) 210 | }) 211 | ``` 212 | 213 | The `boom` methods are also supported 214 | 215 | ```js 216 | mu.define({role: 'some': cmd: 'thing'}, function (args, cb) { 217 | if (!args.pattern.user) { 218 | return cb(mu.error.unauthorized('no user found')) 219 | } 220 | cb(null, {some: 'data'}) 221 | }) 222 | ``` 223 | 224 | See the [boom](http://npm.im) docs for more. 225 | 226 | ## Error Serialization 227 | 228 | `mu.error` uses `boom` and `boom` objects are `Error` objects. 229 | 230 | The native `Error` object has `message` and `stack` properties but they are non-enumerable, which means they don't get serialized (via `JSON.stringify`). This is somewhat awkward in a service-based system. 231 | 232 | By default `mu.error` will make sure these values end up in the stringify output. To turn this off (e.g. perhaps in production) use `serializeErrorProps`: 233 | 234 | ```js 235 | require('mu')({errors: {serializeErrorProps: false}}) 236 | ``` 237 | 238 | ## Custom mu error HTTP status code 239 | 240 | In the event of a mu error, the status code is 500 (internal server error). 241 | 242 | We can set this to default to another code: 243 | 244 | ```js 245 | require('mu')({errors: {httpCode: 509}}) 246 | ``` 247 | 248 | If we want to specify an error code on an individual basis we can use the `mu.error.makeMuError` method directly (see [#api](API)). 249 | 250 | 251 | ## Install 252 | 253 | `mu-error` is exported on the `mu` object, so for general usage we install `mu`: 254 | 255 | ```sh 256 | $ npm install mu 257 | ``` 258 | 259 | Then access 260 | 261 | ```js 262 | const mu = require('mu')() 263 | console.log(mu.error('ah ok')) 264 | ``` 265 | 266 | For internal usage, or testing purposes `mu-error` can also be installed directly: 267 | 268 | ```sh 269 | $ npm install mu-error 270 | ``` 271 | 272 | ## License 273 | Copyright David Mark Clements & Contributors, Licensed under [MIT][]. 274 | 275 | 276 | [mu]: https://github.com/apparatus/mu 277 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 278 | [travis-url]: https://travis-ci.org/apparatus/mu 279 | [npm-badge]: https://badge.fury.io/js/mu.svg 280 | [npm-url]: https://npmjs.org/package/mu 281 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 282 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 283 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 284 | [david-badge]: https://david-dm.org/apparatus/mu.svg 285 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-error 286 | [sponsor]: http://nearform.com 287 | [MIT]: ./LICENSE -------------------------------------------------------------------------------- /packages/mu-error/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var boom = require('boom') 4 | 5 | if (!Error.captureStackTrace) { 6 | Error.captureStackTrace = function noop () {} 7 | } 8 | 9 | module.exports = muError 10 | 11 | var MU_CODES = { 12 | 1: 'service error', 13 | 2: 'framework error', 14 | 3: 'transport error', 15 | 99: 'unknown' 16 | } 17 | var ERRORS = { 18 | SERVICE: 1, 19 | FRAMEWORK: 2, 20 | TRANSPORT: 3, 21 | UNKNOWN: 99 22 | } 23 | 24 | muError.MU_CODES = MU_CODES 25 | muError.ERRORS = ERRORS 26 | muError.extract = extract 27 | 28 | function extract (err) { 29 | if (!err.isMu) { 30 | throw Error('mu-error: extract must have a mu error object') 31 | } 32 | return Object.assign({}, err.output.mu, { 33 | data: err.data, 34 | payload: err.output.payload 35 | }) 36 | } 37 | 38 | function muError (opts) { 39 | opts = opts || {} 40 | var dev = opts.dev 41 | var httpCode = opts.httpCode || 500 42 | var serializeErrorProps = opts.serializeErrorProps !== undefined 43 | ? opts.serializeErrorProps 44 | : true 45 | var maxRemoteStacks = opts.maxRemoteStacks || (dev ? Infinity : 20) 46 | 47 | function mue (statusCode, message, data) { 48 | if (typeof statusCode === 'string' || statusCode === undefined) { 49 | return monoMorphMuError(1, statusCode || '', message || null) 50 | } 51 | 52 | return monoMorphMuError(statusCode, message || '', data || null) 53 | } 54 | 55 | function makeMuError (muCode, httpCode, message, data) { 56 | return ctx(boom.create(httpCode, message, data), muCode, message) 57 | } 58 | 59 | function errorise (error) { 60 | var message = error.message || (error.isMu 61 | ? error.output.mu.message 62 | : error.output.payload.message) 63 | 64 | if (Object.setPrototypeOf) { 65 | Object.setPrototypeOf(error, Error.prototype) 66 | } else { 67 | /* eslint no-proto: 0 */ 68 | error.__proto__ = Error.prototype 69 | } 70 | 71 | Object.defineProperty(error, 'message', { 72 | value: message, 73 | writable: true, 74 | enumerable: false, 75 | configurable: true 76 | }) 77 | if (error.stack) { 78 | Object.defineProperty(error, 'stack', { 79 | value: error.stack, 80 | writable: true, 81 | enumerable: false, 82 | configurable: true 83 | }) 84 | } 85 | 86 | return error 87 | } 88 | 89 | function wrap (error, muCode, statusCode, message, data) { 90 | if (error == null) { return error } 91 | if (!(error instanceof Error)) { 92 | // probably been serialized: 93 | if (error.isBoom) { 94 | error = errorise(error) 95 | } else { 96 | throw Error('mu-error: must be an Error or Boom object') 97 | } 98 | } 99 | var alreadyIs = error.isMu && error.output && error.output.mu && error.output.mu.code === muCode 100 | if (alreadyIs) { 101 | return error 102 | } 103 | muCode = muCode || 1 104 | if (error.isBoom) { 105 | var msg = (error.output.mu ? error.output.mu.message : error.output.payload.message) 106 | message = (message && msg 107 | ? message + ': ' + msg 108 | : msg) || message 109 | return ctx(error, muCode, message) 110 | } 111 | var err = boom.wrap(error, statusCode || httpCode, message) 112 | return ctx(err, muCode, error.message) 113 | } 114 | 115 | function wrapRemote (error, muCode, statusCode, message, data) { 116 | if (error == null) { return error } 117 | if (error.output && error.output.mu) { 118 | muCode = muCode || error.output.mu.code 119 | } 120 | wrap(error, muCode, statusCode, message, data) 121 | error.remoteStacks = error.remoteStacks || [] 122 | 123 | if (error.stack) { 124 | if (error.remoteStacks.length >= maxRemoteStacks) { 125 | error.remoteStacks.splice(0, error.remoteStacks.length - maxRemoteStacks + 1) 126 | } 127 | error.remoteStacks.push({ 128 | timestamp: Date.now(), 129 | stack: error.stack 130 | }) 131 | } 132 | error.stack = Error(error.message).stack 133 | preserveErrorInfo(error) 134 | return error 135 | } 136 | 137 | Object.assign(mue, boom) 138 | mue.create = mue 139 | mue.wrap = wrap 140 | mue.remote = wrapRemote 141 | mue.wrapRemote = wrapRemote 142 | mue.makeMuError = makeMuError 143 | mue.service = makeConvenienceMethod(ERRORS.SERVICE) 144 | mue.framework = makeConvenienceMethod(ERRORS.FRAMEWORK) 145 | mue.transport = makeConvenienceMethod(ERRORS.TRANSPORT) 146 | mue.extract = extract 147 | mue.MU_CODES = MU_CODES 148 | mue.ERRORS = ERRORS 149 | 150 | return mue 151 | 152 | function makeConvenienceMethod (code) { 153 | return function (message, data) { 154 | if (message instanceof Error) { 155 | if (message.isBoom) { 156 | message.output.payload.message = message.isMu ? message.output.mu.message : message.message 157 | message.isMu = false 158 | } 159 | return wrap(message, code, httpCode, undefined, data) 160 | } 161 | return mue(code, message, data) 162 | } 163 | } 164 | 165 | function ctx (err, muCode, message) { 166 | err.output.mu = { 167 | code: muCode, 168 | error: MU_CODES[muCode] || MU_CODES[99], 169 | message: message 170 | } 171 | if (dev) { 172 | err.output.payload.mu = err.output.mu 173 | if (err.data) { 174 | err.output.payload.mu.data = err.data 175 | } 176 | } 177 | if (!err.isMu) { preserveErrorInfo(err) } 178 | err.isMu = true 179 | return err 180 | } 181 | 182 | function monoMorphMuError (statusCode, message, data) { 183 | if (statusCode === 0 || statusCode >= 100 && statusCode < 400) { 184 | throw Error('first argument must be 1..99 or 400+') 185 | } 186 | message = message || undefined 187 | if (statusCode < 100) { 188 | return makeMuError(statusCode, httpCode, message, data) 189 | } 190 | var err = boom.create(statusCode, message, data) 191 | return preserveErrorInfo(err) 192 | } 193 | 194 | function preserveErrorInfo (err) { 195 | if (!serializeErrorProps || err.__preserving__) { return err } 196 | Object.defineProperty(err, '__preserving__', {value: true}) 197 | Object.defineProperty(err, 'toJSON', {value: errToJson}) 198 | return err 199 | } 200 | } 201 | 202 | function errToJson () { 203 | // have to reconfigure properties, to make them enumerable 204 | Object.defineProperty(this, 'stack', errJsonReset(this, 'stack')) 205 | Object.defineProperty(this, 'message', errJsonReset(this, 'message')) 206 | 207 | return this 208 | } 209 | 210 | function errJsonReset (o, k) { 211 | return { 212 | value: { 213 | self: o, 214 | value: o[k], 215 | toJSON: selfReset 216 | }, 217 | enumerable: true 218 | } 219 | } 220 | 221 | function selfReset (k) { 222 | Object.defineProperty(this.self, k, { 223 | value: this.value, 224 | enumerable: false 225 | }) 226 | return this.value 227 | } 228 | -------------------------------------------------------------------------------- /packages/mu-error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-error", 3 | "version": "1.4.5", 4 | "description": "The official error handling library for mu, provides distributed error handling functionality", 5 | "main": "index.js", 6 | "scripts": { 7 | "browser-test": "zuul tape test --local", 8 | "cov": "tap test --cov", 9 | "html-cov": "tap test --coverage-report=html", 10 | "ci": "standard && tap test --coverage-report=lcov && codecov", 11 | "test": "tap test", 12 | "lint": "standard" 13 | }, 14 | "pre-commit": [ 15 | "lint", 16 | "test" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/apparatus/mu.git" 21 | }, 22 | "keywords": [], 23 | "author": "David Mark Clements", 24 | "license": "MIT", 25 | "dependencies": { 26 | "boom": "^4.2.0" 27 | }, 28 | "devDependencies": { 29 | "codecov": "^1.0.1", 30 | "pre-commit": "^1.1.3", 31 | "standard": "^8.5.0", 32 | "tap": "^8.0.0", 33 | "tape": "^4.6.2", 34 | "zuul": "^3.11.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/mu-error/test/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var test = require('tape') 3 | var mue = require('../') 4 | var MU_CODES = mue.MU_CODES 5 | var ERRORS = mue.ERRORS 6 | var ctrl = { 7 | cache: require.cache || arguments[5], 8 | mue: require.resolve ? require.resolve('..') : path.resolve(arguments[6][0], '..', 'index.js') 9 | } 10 | 11 | // must be first test 12 | test('patches Error.captureStackTrack for cross browser support', function (t) { 13 | delete ctrl.cache[ctrl.mue] 14 | var captureStackTrace = Error.captureStackTrace 15 | delete Error.captureStackTrace 16 | require('../') 17 | t.ok(Error.captureStackTrace) 18 | t.is(fnName(Error.captureStackTrace), 'noop') 19 | Error.captureStackTrace() 20 | if (captureStackTrace) Error.captureStackTrace = captureStackTrace 21 | t.end() 22 | }) 23 | 24 | test('returns a valid boom object', function (t) { 25 | var muErr = mue() 26 | var err = muErr(400, 'test', {test: 'data'}) 27 | t.ok(err instanceof Error) 28 | t.is(err.isBoom, true) 29 | t.is(err.output.statusCode, 400) 30 | t.deepEqual(err.data, {test: 'data'}) 31 | t.is(err.output.payload.message, 'test') 32 | t.end() 33 | }) 34 | 35 | test('throws if statusCode is not 1..99 or 400+', function (t) { 36 | var muErr = mue() 37 | t.throws(function () { muErr(300) }) 38 | t.end() 39 | }) 40 | 41 | test('converts code < 100 to 500 status code', function (t) { 42 | var muErr = mue() 43 | var err = muErr(ERRORS.SERVICE) 44 | t.is(err.output.statusCode, 500) 45 | t.end() 46 | }) 47 | 48 | test('adds isMu to when code < 100', function (t) { 49 | var muErr = mue() 50 | t.is(muErr(ERRORS.SERVICE).isMu, true) 51 | t.is(muErr.create(ERRORS.SERVICE).isMu, true) 52 | t.end() 53 | }) 54 | 55 | test('adds mu error object when code < 100', function (t) { 56 | var muErr = mue() 57 | var err = muErr(ERRORS.SERVICE) 58 | t.deepEqual( 59 | err.output.mu, 60 | { code: ERRORS.SERVICE, error: MU_CODES[ERRORS.SERVICE], message: undefined } 61 | ) 62 | t.end() 63 | }) 64 | 65 | test('in dev mode, adds mu error object to payload when code < 100', function (t) { 66 | var muErr = mue({dev: true}) 67 | var err = muErr(ERRORS.SERVICE) 68 | t.deepEqual( 69 | err.output.payload.mu, 70 | { code: ERRORS.SERVICE, error: MU_CODES[ERRORS.SERVICE], message: undefined } 71 | ) 72 | t.end() 73 | }) 74 | 75 | test('in dev mode, adds data to mu error object when data present', function (t) { 76 | var muErr = mue({dev: true}) 77 | var err = muErr(1, 'test', {data: 'test'}) 78 | t.deepEqual( 79 | err.output.payload.mu, 80 | { code: ERRORS.SERVICE, error: MU_CODES[ERRORS.SERVICE], message: 'test', data: {data: 'test'} } 81 | ) 82 | t.end() 83 | }) 84 | 85 | test('defaults to error code 1', function (t) { 86 | var muErr = mue() 87 | t.deepEqual( 88 | muErr().output.mu, 89 | { code: ERRORS.SERVICE, error: MU_CODES[ERRORS.SERVICE], message: undefined } 90 | ) 91 | t.deepEqual( 92 | muErr('test').output.mu, 93 | { code: ERRORS.SERVICE, error: MU_CODES[ERRORS.SERVICE], message: 'test' } 94 | ) 95 | t.end() 96 | }) 97 | 98 | test('mue.wrap converts Error object', function (t) { 99 | var muErr = mue() 100 | var err = Error('test') 101 | muErr.wrap(err, 2, 509, 'test') 102 | t.is(err.isBoom, true) 103 | t.is(err.isMu, true) 104 | t.is(err.output.mu.code, 2) 105 | t.is(err.output.mu.message, 'test: test') 106 | 107 | var result = muErr.wrap(Error('test')) 108 | t.is(result.isBoom, true) 109 | t.is(result.isMu, true) 110 | t.is(result.output.mu.code, 1) 111 | t.is(result.output.mu.message, 'test') 112 | 113 | t.end() 114 | }) 115 | 116 | test('mue.wrap wraps Boom object', function (t) { 117 | var muErr = mue() 118 | var err = muErr(400, 'test') 119 | muErr.wrap(err, 2, 400, 'test') 120 | t.is(err.isBoom, true) 121 | t.is(err.isMu, true) 122 | t.is(err.output.mu.code, 2) 123 | t.is(err.output.mu.message, 'test: test') 124 | 125 | var result = muErr.wrap(muErr(400, 'test')) 126 | t.is(result.isBoom, true) 127 | t.is(result.isMu, true) 128 | t.is(result.output.mu.code, 1) 129 | t.is(result.output.mu.message, 'test') 130 | 131 | result = muErr.wrap(muErr(400, 'test')) 132 | 133 | t.end() 134 | }) 135 | 136 | test('mue.wrap wraps serialized-deserialized Boom object', function (t) { 137 | var muErr = mue() 138 | var err = JSON.parse(JSON.stringify(muErr(400, 'test'))) 139 | muErr.wrap(err, 2, 400, 'test') 140 | t.is(err.isBoom, true) 141 | t.is(err.isMu, true) 142 | t.is(err.output.mu.code, 2) 143 | t.is(err.output.mu.message, 'test: test') 144 | t.is(err.message, 'test') 145 | t.ok(err.stack) 146 | 147 | var result = muErr.wrap(JSON.parse(JSON.stringify(muErr(400, 'test')))) 148 | t.is(result.isBoom, true) 149 | t.is(result.isMu, true) 150 | t.is(result.output.mu.code, 1) 151 | t.is(result.output.mu.message, 'test') 152 | t.is(result.message, 'test') 153 | t.ok(err.stack) 154 | 155 | result = muErr.wrap(JSON.parse(JSON.stringify(muErr('test')))) 156 | t.is(result.isBoom, true) 157 | t.is(result.isMu, true) 158 | t.is(result.output.mu.code, 1) 159 | t.is(result.output.mu.message, 'test') 160 | t.is(result.message, 'test') 161 | t.ok(err.stack) 162 | 163 | // message fallbacks 164 | err = JSON.parse(JSON.stringify(muErr('test'))) 165 | err.message = '' 166 | result = muErr.wrap(err) 167 | t.is(result.output.mu.message, 'test') 168 | t.is(result.message, 'test') 169 | 170 | err = JSON.parse(JSON.stringify(muErr('test'))) 171 | err.message = '' 172 | err.isMu = false 173 | result = muErr.wrap(err) 174 | t.is(result.output.mu.message, 'test') 175 | t.is(result.message, 'An internal server error occurred') 176 | 177 | // force set proto fallback: 178 | 179 | var setPrototypeOf = Object.setPrototypeOf 180 | Object.setPrototypeOf = null 181 | result = muErr.wrap(JSON.parse(JSON.stringify(muErr('test')))) 182 | t.is(result.isBoom, true) 183 | t.is(result.isMu, true) 184 | t.is(result.output.mu.code, 1) 185 | t.is(result.output.mu.message, 'test') 186 | t.is(result.message, 'test') 187 | Object.setPrototypeOf = setPrototypeOf 188 | 189 | t.end() 190 | }) 191 | 192 | test('mue.wrap immediately returns mu error objects with the same muCode', function (t) { 193 | var muErr = mue({serializeErrorProps: false}) 194 | var err = muErr(3) 195 | var before = JSON.stringify(err.output.mu) 196 | var result = muErr.wrap(err, 3) 197 | t.is(JSON.stringify(result.output.mu), before) 198 | 199 | err = muErr() 200 | before = JSON.stringify(err.output.mu) 201 | result = muErr.wrap(err) 202 | t.is(JSON.stringify(result.output.mu), before) 203 | t.end() 204 | }) 205 | 206 | test('mue.wrap immediately returns mu error objects when input muCode is different', function (t) { 207 | var muErr = mue() 208 | var err = muErr(3) 209 | var before = JSON.stringify(err) 210 | var result = muErr.wrap(err, 3) 211 | t.is(before, JSON.stringify(result)) 212 | 213 | err = muErr() 214 | before = JSON.stringify(err) 215 | result = muErr.wrap(err) 216 | t.is(before, JSON.stringify(result)) 217 | t.end() 218 | }) 219 | 220 | test('mue.wrap immediately returns errors that are null or undefined', function (t) { 221 | var muErr = mue() 222 | t.is(muErr.wrap(null), null) 223 | t.is(muErr.wrap(undefined), undefined) 224 | t.end() 225 | }) 226 | 227 | test('mue.wrap throws if err is not instanceof Error, or null, or undefined', function (t) { 228 | t.throws(() => mue().wrap({err: 'oh oh'})) 229 | t.end() 230 | }) 231 | 232 | test('mue.remoteWrap immediately returns errors that are null or undefined', function (t) { 233 | var muErr = mue() 234 | t.is(muErr.wrapRemote(null), null) 235 | t.is(muErr.wrapRemote(undefined), undefined) 236 | t.end() 237 | }) 238 | 239 | test('mue.wrapRemote manages remote and local stacks', function (t) { 240 | var muErr = mue() 241 | var err = JSON.parse(JSON.stringify(muErr('test'))) 242 | muErr.wrapRemote(err, 2, 400, 'test') 243 | t.is(err.isBoom, true) 244 | t.is(err.isMu, true) 245 | t.is(err.output.mu.code, 2) 246 | t.is(err.output.mu.message, 'test: test') 247 | t.is(err.message, 'test') 248 | t.ok(err.stack) 249 | t.ok(Array.isArray(err.remoteStacks)) 250 | t.is(err.remoteStacks.length, 1) 251 | err = JSON.parse(JSON.stringify(err)) 252 | muErr.wrapRemote(err, 2, 400, 'test') 253 | t.is(err.remoteStacks.length, 2) 254 | err = JSON.parse(JSON.stringify(err)) 255 | muErr.wrapRemote(err, 3, 400, 'test') 256 | t.is(err.remoteStacks.length, 3) 257 | t.is(err.output.mu.code, 3) 258 | t.end() 259 | }) 260 | 261 | test('mue.remoteWrap uses prexisting code on mu error object if muCode is absent', function (t) { 262 | var muErr = mue() 263 | var err = JSON.parse(JSON.stringify(muErr(3))) 264 | muErr.wrapRemote(err) 265 | t.is(err.output.mu.code, 3) 266 | 267 | err = JSON.parse(JSON.stringify(muErr(3))) 268 | delete err.output.mu 269 | muErr.wrapRemote(err) 270 | t.is(err.output.mu.code, 1) 271 | 272 | err = JSON.parse(JSON.stringify(muErr(3))) 273 | delete err.output.mu 274 | muErr.wrapRemote(err) 275 | t.is(err.output.mu.code, 1) 276 | 277 | err = JSON.parse(JSON.stringify(muErr(3))) 278 | delete err.output.mu 279 | muErr.wrapRemote(err, 2) 280 | t.is(err.output.mu.code, 2) 281 | 282 | t.end() 283 | }) 284 | 285 | test('mue.wrapRemote always adds remoteStacks property', function (t) { 286 | var muErr = mue() 287 | var err = JSON.parse(JSON.stringify(muErr('test'))) 288 | delete err.stack 289 | muErr.wrapRemote(err, 2, 400, 'test') 290 | t.ok(Array.isArray(err.remoteStacks)) 291 | err = JSON.parse(JSON.stringify(err)) 292 | delete err.stack 293 | muErr.wrapRemote(err, 2, 400, 'test') 294 | t.is(err.remoteStacks.length, 0) 295 | t.end() 296 | }) 297 | 298 | test('mue.wrapRemote does not add to remoteStacks if no stack property', function (t) { 299 | var muErr = mue() 300 | var err = JSON.parse(JSON.stringify(muErr('test'))) 301 | delete err.stack 302 | muErr.wrapRemote(err, 2, 400, 'test') 303 | t.is(err.remoteStacks.length, 0) 304 | t.end() 305 | }) 306 | 307 | test('mue.extract provides mu context at top level, with payload and data keys', function (t) { 308 | t.deepEqual(mue.extract(mue()('test')), { 309 | code: 1, 310 | error: 'service error', 311 | message: 'test', 312 | data: null, 313 | payload: { 314 | statusCode: 500, 315 | error: 'Internal Server Error', 316 | message: 'An internal server error occurred' 317 | } 318 | }) 319 | t.end() 320 | }) 321 | 322 | test('mue.extract throws if err is a mu error object', function (t) { 323 | t.throws(() => mue.extract({err: 'oh oh'})) 324 | t.throws(() => mue().extract(mue()(400))) 325 | t.end() 326 | }) 327 | 328 | test('unknown mu code results in "unknown" error type', function (t) { 329 | var muErr = mue() 330 | t.deepEqual( 331 | muErr(99).output.mu, 332 | { code: 99, error: MU_CODES[99], message: undefined } 333 | ) 334 | t.deepEqual( 335 | muErr(98).output.mu, 336 | { code: 98, error: MU_CODES[99], message: undefined } 337 | ) 338 | t.end() 339 | }) 340 | 341 | test('mue.service returns error with mu code 1', function (t) { 342 | var muErr = mue() 343 | t.deepEqual( 344 | muErr.service('test').output.mu, 345 | { code: ERRORS.SERVICE, error: MU_CODES[ERRORS.SERVICE], message: 'test' } 346 | ) 347 | t.end() 348 | }) 349 | 350 | test('mue.framework returns error with mu code 2', function (t) { 351 | var muErr = mue() 352 | t.deepEqual( 353 | muErr.framework('test').output.mu, 354 | { code: ERRORS.FRAMEWORK, error: MU_CODES[ERRORS.FRAMEWORK], message: 'test' } 355 | ) 356 | t.end() 357 | }) 358 | 359 | test('mue.transport returns error with mu code 3', function (t) { 360 | var muErr = mue() 361 | t.deepEqual( 362 | muErr.transport('test').output.mu, 363 | { code: ERRORS.TRANSPORT, error: MU_CODES[ERRORS.TRANSPORT], message: 'test' } 364 | ) 365 | t.end() 366 | }) 367 | 368 | test('convenience methods wrap Error and boom objects', function (t) { 369 | var muErr = mue() 370 | var err = muErr.service(Error('test')) 371 | t.deepEqual( 372 | err.output.mu, 373 | { code: ERRORS.SERVICE, error: MU_CODES[ERRORS.SERVICE], message: 'test' } 374 | ) 375 | var err2 = muErr.framework(err) 376 | t.deepEqual( 377 | err2.output.mu, 378 | { code: ERRORS.FRAMEWORK, error: MU_CODES[ERRORS.FRAMEWORK], message: 'test' } 379 | ) 380 | var err3 = muErr.transport(err2) 381 | t.deepEqual( 382 | err3.output.mu, 383 | { code: ERRORS.TRANSPORT, error: MU_CODES[ERRORS.TRANSPORT], message: 'test' } 384 | ) 385 | var err4 = muErr.transport(muErr(400)) 386 | t.deepEqual( 387 | err4.output.mu, 388 | { code: ERRORS.TRANSPORT, error: MU_CODES[ERRORS.TRANSPORT], message: 'Bad Request' } 389 | ) 390 | var err5 = muErr.transport(muErr()) 391 | t.deepEqual( 392 | err5.output.mu, 393 | { code: ERRORS.TRANSPORT, error: MU_CODES[ERRORS.TRANSPORT], message: undefined } 394 | ) 395 | t.end() 396 | }) 397 | 398 | test('serializeErrorProps option serializes stack and message when true', function (t) { 399 | var muErr = mue({serializeErrorProps: false}) 400 | var o = JSON.parse(JSON.stringify(muErr('test'))) 401 | t.notOk(o.stack) 402 | t.notOk(o.message) 403 | muErr = mue({serializeErrorProps: true}) 404 | o = JSON.parse(JSON.stringify(muErr('test'))) 405 | t.ok(o.stack) 406 | t.is(o.message, 'test') 407 | t.end() 408 | }) 409 | 410 | test('serializeErrorProps option defaults to true', function (t) { 411 | var muErr = mue() 412 | var o = JSON.parse(JSON.stringify(muErr('test'))) 413 | t.ok(o.stack) 414 | t.is(o.message, 'test') 415 | t.end() 416 | }) 417 | 418 | test('maxRemoteStacks option controls total amount of remote stacks', function (t) { 419 | var muErr = mue({maxRemoteStacks: 3}) 420 | var err = JSON.parse(JSON.stringify(muErr('test'))) 421 | muErr.wrapRemote(err) 422 | t.ok(Array.isArray(err.remoteStacks)) 423 | t.is(err.remoteStacks.length, 1) 424 | err = JSON.parse(JSON.stringify(err)) 425 | muErr.wrapRemote(err) 426 | t.is(err.remoteStacks.length, 2) 427 | err = JSON.parse(JSON.stringify(err)) 428 | muErr.wrapRemote(err) 429 | t.is(err.remoteStacks.length, 3) 430 | err = JSON.parse(JSON.stringify(err)) 431 | muErr.wrapRemote(err) 432 | t.is(err.remoteStacks.length, 3) 433 | err = JSON.parse(JSON.stringify(err)) 434 | muErr.wrapRemote(err) 435 | t.is(err.remoteStacks.length, 3) 436 | t.end() 437 | }) 438 | 439 | function fnName (fn) { 440 | var rx = /^\s*function\s*([^\(]*)/i 441 | var match = rx.exec(fn) 442 | return match && match[1] 443 | } 444 | -------------------------------------------------------------------------------- /packages/mu-http/README.md: -------------------------------------------------------------------------------- 1 | # mu-http 2 | 3 | [![npm][npm-badge]][npm-url] 4 | [![travis][travis-badge]][travis-url] 5 | [![coveralls][coveralls-badge]][coveralls-url] 6 | [![david][david-badge]][david-url] 7 | 8 | - __Sponsor:__ [nearForm][sponsor] 9 | - __Status:__ UNFINISHED - DO NOT USE 10 | 11 | Part of the Official [mu][Mu Suite]. 12 | 13 | `mu-http` is an HTTP transport driver for `mu`, it allows for message dispatching across HTTP. 14 | 15 | 16 | * [Install](#install) 17 | * [API](#api) 18 | * [License](#license) 19 | 20 | 21 | ## Install 22 | 23 | ```sh 24 | $ npm install mu-http 25 | ``` 26 | 27 | ## API 28 | To do.. 29 | 30 | ## License 31 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 32 | 33 | 34 | [mu]: https://github.com/apparatus/mu 35 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 36 | [travis-url]: https://travis-ci.org/apparatus/mu 37 | [npm-badge]: https://badge.fury.io/js/mu.svg 38 | [npm-url]: https://npmjs.org/package/mu 39 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 40 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 41 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 42 | [david-badge]: https://david-dm.org/apparatus/mu.svg 43 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-http 44 | [sponsor]: http://nearform.com 45 | [MIT]: ./LICENSE 46 | -------------------------------------------------------------------------------- /packages/mu-http/driver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var http = require('http') 18 | var stringify = require('fast-safe-stringify') 19 | var parse = require('fast-json-parse') 20 | var assert = require('assert') 21 | var mue = require('mu-error')() 22 | 23 | 24 | /** 25 | * HTTP transport 26 | * Figure out path mapping - perhaps this may need to be part of the config for now just / 27 | */ 28 | module.exports = function createHttpDriver (options) { 29 | return function httpDriver (opts, receive) { 30 | var connections = {} 31 | var connectionsByIp = {} 32 | var server 33 | 34 | assert(opts, 'transport should always pass opts to httpDriver') 35 | assert(receive instanceof Function, 'transport should always pass receive function to httpDriver') 36 | 37 | server = options.source && listen(options.source.port, options.source.host, options.ready) 38 | 39 | if (!server && options.ready instanceof Function) { 40 | options.ready() 41 | } 42 | 43 | 44 | 45 | function send (message, cb) { 46 | var sendTo 47 | 48 | if (connections[message.protocol.dst]) { 49 | 50 | // server responding to client initaiated connection 51 | if (connections[message.protocol.dst].length > 0) { 52 | sendTo = connections[message.protocol.dst].pop() 53 | sendTo.response.writeHead(200, { 'Content-Type': 'application/json' }) 54 | sendTo.response.write(stringify(message)) 55 | sendTo.response.end() 56 | } else { 57 | cb(mue.transport('connection has alredy recieved response!')) 58 | } 59 | } else { 60 | 61 | // client initiating connection to server 62 | var body = [] 63 | var inbound 64 | 65 | var req = http.request({host: options.target.host, 66 | port: options.target.port, 67 | method: 'POST', 68 | path: '/mu/', 69 | headers: {'Content-Type': 'application/json'}}) 70 | 71 | req.on('response', function (response) { 72 | response.on('data', function (chunk) { 73 | body.push(chunk) 74 | }) 75 | 76 | response.on('end', function () { 77 | if (response.statusCode !== 200) { 78 | receive(mue.transport(response.statusCode + ' ' + response.statusMessage), message) 79 | return 80 | } 81 | 82 | inbound = parse(Buffer.concat(body)) 83 | receive(inbound.err, inbound.value) 84 | }) 85 | }) 86 | 87 | req.on('error', function (err) { 88 | cb(mue.transport(err)) 89 | }) 90 | 91 | req.end(stringify(message)) 92 | } 93 | } 94 | 95 | 96 | 97 | function listen (port, host, ready) { 98 | return http.createServer(function (request, response) { 99 | var body = [] 100 | var inbound 101 | 102 | request.on('data', function (chunk) { 103 | body.push(chunk) 104 | }) 105 | 106 | request.on('end', function () { 107 | inbound = parse(Buffer.concat(body)) 108 | 109 | assert(inbound.value.protocol.src) 110 | 111 | if (!connections[inbound.value.protocol.src]) { 112 | connections[inbound.value.protocol.src] = [] 113 | connectionsByIp[request.socket.remoteAddress + '_' + request.socket.remotePort] = [] 114 | } 115 | connections[inbound.value.protocol.src].push({request: request, response: response}) 116 | connectionsByIp[request.socket.remoteAddress + '_' + request.socket.remotePort] = inbound.value.protocol.src 117 | receive(inbound.err, inbound.value) 118 | }) 119 | }).listen(port, host, ready) 120 | } 121 | 122 | 123 | 124 | function tearDown (cb) { 125 | for (var conn in connections) { 126 | if (connections[conn] && connections[conn].length > 0) { 127 | var c = connections[conn].pop() 128 | c.response.end() 129 | c.request.destroy() 130 | } 131 | } 132 | if (!server && cb) { 133 | cb() 134 | return 135 | } 136 | if (server) { 137 | if (cb) { 138 | 139 | // should not be needed investigate 140 | setImmediate(function () { 141 | server.close(cb) 142 | }) 143 | return 144 | } 145 | server.close() 146 | } 147 | } 148 | 149 | 150 | 151 | return { 152 | type: 'http', 153 | send: send, 154 | tearDown: tearDown 155 | } 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /packages/mu-http/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var transport = require('mu-transport') 18 | var httpDriver = require('./driver') 19 | 20 | module.exports = { 21 | server: function server (source, ready) { 22 | return function driver (mu, opts) { 23 | var drv = httpDriver({source: source, id: opts.id, ready: ready}) 24 | return transport(drv, mu, opts) 25 | } 26 | }, 27 | client: function client (target, ready) { 28 | return function driver (mu, opts) { 29 | var drv = httpDriver({target: target, id: opts.id, ready: ready}) 30 | return transport(drv, mu, opts) 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /packages/mu-http/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-http", 3 | "version": "1.0.5", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [ 7 | "http", 8 | "mu", 9 | "transport", 10 | "microservices", 11 | "messaging", 12 | "router", 13 | "distributed" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/apparatus/mu.git" 18 | }, 19 | "author": "Peter Elger (https://github.com/pelger)", 20 | "contributors": [ 21 | "David Mark Clements (https://github.com/davidmarkclements)", 22 | "Dean McDonnell (https://github.com/mcdonnelldean)", 23 | "Matteo Collina (https://github.com/mcollina)" 24 | ], 25 | "license": "MIT", 26 | "dependencies": { 27 | "fast-json-parse": "^1.0.2", 28 | "fast-safe-stringify": "^1.1.1", 29 | "mu-error": "^1.4.5", 30 | "mu-transport": "^1.0.9" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/mu-local/README.md: -------------------------------------------------------------------------------- 1 | # mu-local 2 | 3 | The official local transport driver for mu, it allows for in-process/in-browser message dispatching 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-local` is a local transport driver for `mu`, it allows for in-process/in-browser message dispatching. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-local 27 | ``` 28 | 29 | ## API 30 | To do.. 31 | 32 | ## License 33 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 34 | 35 | 36 | [mu]: https://github.com/apparatus/mu 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-local 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu-local/driver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var assert = require('assert') 18 | 19 | var register = {} 20 | 21 | /** 22 | * local function transport. 23 | * uses a singleton transport registry to connect to in 24 | * process mu instances 25 | */ 26 | module.exports = function createLocalDriver (options) { 27 | return function localDriver (opts, receive) { 28 | 29 | var target = options && options.target && 30 | options.target.transports().filter(function (transport) { 31 | return (transport.driver && 32 | transport.driver.type === 'func') 33 | }) 34 | target = target && target[target.length - 1].driver 35 | 36 | assert(opts, 'transport should always pass opts to localDriver') 37 | assert(receive instanceof Function, 'transport should always pass receive function to localDriver') 38 | 39 | var id = opts.id 40 | 41 | 42 | register[id] = { 43 | type: 'func', 44 | send: send, 45 | call: call, 46 | tearDown: tearDown 47 | } 48 | 49 | return register[id] 50 | 51 | function send (message, cb) { 52 | var tx = message.protocol.dst === 'target' 53 | ? target 54 | : register[message.protocol.dst] 55 | tx.call(message) 56 | } 57 | 58 | function call (message) { 59 | receive(null, message) 60 | } 61 | 62 | function tearDown (cb) { 63 | if (cb) cb() 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /packages/mu-local/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var transport = require('mu-transport') 18 | var funcDriver = require('./driver') 19 | 20 | module.exports = function (options) { 21 | return function driver (mu, opts) { 22 | return transport(funcDriver(options), mu, opts) 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /packages/mu-local/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-local", 3 | "version": "1.0.9", 4 | "description": "An official local transport driver for mu, it allows for in-process/in-browser message dispatching", 5 | "main": "index.js", 6 | "keywords": [ 7 | "local", 8 | "mu", 9 | "transport", 10 | "microservices", 11 | "messaging", 12 | "router", 13 | "distributed" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/apparatus/mu.git" 18 | }, 19 | "author": "Peter Elger (https://github.com/pelger)", 20 | "contributors": [ 21 | "David Mark Clements (https://github.com/davidmarkclements)", 22 | "Dean McDonnell (https://github.com/mcdonnelldean)", 23 | "Matteo Collina (https://github.com/mcollina)" 24 | ], 25 | "dependencies": { 26 | "mu-transport": "^1.0.9" 27 | }, 28 | "license": "MIT" 29 | } 30 | -------------------------------------------------------------------------------- /packages/mu-redis/README.md: -------------------------------------------------------------------------------- 1 | # mu-redis 2 | 3 | The official Redis transport driver for mu 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-redis` is a Redis transport driver for `mu`, it allows for message dispatching across the Redis PubSub system. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-redis 27 | ``` 28 | 29 | ## API 30 | To do.. 31 | 32 | ## License 33 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 34 | 35 | 36 | [mu]: https://github.com/apparatus/mu 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-redis 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu-redis/driver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var EventEmitter = require('events') 18 | var stringify = require('fast-safe-stringify') 19 | var assert = require('assert') 20 | var redis = require('redis') 21 | 22 | 23 | /** 24 | * options 25 | * { 26 | * { host, port, list, redis - provide test redis client instance } 27 | * } 28 | * list should be service name, the driver will use two lists 29 | * _req 30 | * _res 31 | * servers (services) will listen on _req and send on _res 32 | * clients (service invokers) will send on _req and listen on _res 33 | */ 34 | module.exports = function createRedisDriver (options) { 35 | return function redisDriver (opts, cb) { 36 | var emitter = new EventEmitter() 37 | var tearingDown = false 38 | var rin = null 39 | var rout = null 40 | 41 | assert(opts, 'transport should always pass opts to redis driver') 42 | assert(cb, 'transport should always pass cb to redis driver') 43 | 44 | emitter.on('receive', cb) 45 | 46 | assert(options, 'redis driver requires an options object') 47 | 48 | if (options.target) { 49 | rin = redis.createClient(options.target.port, options.target.host) 50 | rout = redis.createClient(options.target.port, options.target.host) 51 | } 52 | if (options.source) { 53 | rin = redis.createClient(options.source.port, options.source.host) 54 | rout = redis.createClient(options.source.port, options.source.host) 55 | } 56 | 57 | listen() 58 | 59 | return { 60 | type: 'redis', 61 | send: send, 62 | tearDown: tearDown 63 | } 64 | 65 | 66 | 67 | function listen (cb) { 68 | var listName 69 | 70 | if (options.source) { 71 | listName = options.source.list + '_req' 72 | } 73 | if (options.target) { 74 | // listName = options.target.list + '_res' 75 | listName = options.target.list + '_res_' + opts.id 76 | } 77 | 78 | var brpopQueue = function () { 79 | rin.brpop(listName, 5, function (err, data) { 80 | if (cb && err) { 81 | return cb(err) 82 | } else if (data) { 83 | var message = JSON.parse(data[1]) 84 | emitter.emit('receive', null, message) 85 | } 86 | if (!tearingDown) { 87 | brpopQueue() 88 | } 89 | }) 90 | } 91 | brpopQueue() 92 | } 93 | 94 | 95 | 96 | function send (message, cb) { 97 | if (options.source) { 98 | rout.lpush(options.source.list + '_res_' + message.protocol.dst, stringify(message), function (err) { 99 | cb && cb(err) 100 | }) 101 | } 102 | if (options.target) { 103 | rout.lpush(options.target.list + '_req', stringify(message), function (err) { 104 | cb && cb(err) 105 | }) 106 | } 107 | } 108 | 109 | 110 | 111 | function tearDown () { 112 | tearingDown = true 113 | rin.end(true) 114 | rout.end(true) 115 | } 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /packages/mu-redis/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var transport = require('mu-transport') 18 | var driver = require('./driver') 19 | 20 | module.exports = { 21 | server: function server (source) { 22 | return function redisDriver (mu, opts) { 23 | var drv = driver({source: source, id: opts.id}) 24 | return transport(drv, mu, opts) 25 | } 26 | }, 27 | client: function client (target) { 28 | return function redisDriver (mu, opts) { 29 | var drv = driver({target: target, id: opts.id}) 30 | return transport(drv, mu, opts) 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /packages/mu-redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-redis", 3 | "version": "1.0.9", 4 | "description": "The official Redis transport driver for mu", 5 | "main": "index.js", 6 | "keywords": [ 7 | "redis", 8 | "mu", 9 | "transport", 10 | "microservices", 11 | "messaging", 12 | "router", 13 | "distributed" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/apparatus/mu.git" 18 | }, 19 | "author": "Peter Elger (https://github.com/pelger)", 20 | "contributors": [ 21 | "David Mark Clements (https://github.com/davidmarkclements)", 22 | "Dean McDonnell (https://github.com/mcdonnelldean)", 23 | "Matteo Collina (https://github.com/mcollina)" 24 | ], 25 | "license": "MIT", 26 | "dependencies": { 27 | "fast-safe-stringify": "^1.1.1", 28 | "mu-transport": "^1.0.9", 29 | "redis": "^2.6.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/mu-router/README.md: -------------------------------------------------------------------------------- 1 | # mu-router 2 | 3 | The official pattern router for mu 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-router` is the pattern router for [mu][`mu`], used internally by [mu][`mu`] core it manages a routing table based on Bloom filters. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-router 27 | ``` 28 | 29 | ## API 30 | To do.. 31 | 32 | ## License 33 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 34 | 35 | 36 | [mu]: https://github.com/apparatus/mu 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-router 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu-router/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var bloomrun = require('bloomrun') 18 | var assert = require('assert') 19 | var stringify = require('fast-safe-stringify') 20 | var parallel = require('fastparallel')() 21 | 22 | var MALFORMED_PACKET = 'Malformed packet no pattern or response field. Message will be discarded' 23 | var NO_MATCHING_ROUTE = 'Routing error: no matching route and no default route provided, Message will be discarded' 24 | var INVALID_OUTBOUND_ROUTE = 'Routing error: no valid outbound route available. Message will be discarded' 25 | var NO_AVAILABLE_TRANSPORT = 'Routing error: no available transport function' 26 | 27 | /** 28 | * pattern router. responsible for the routing table 29 | */ 30 | module.exports = function (opts) { 31 | var logger = opts.logger 32 | var mue = opts.mue 33 | var run = bloomrun({indexing: 'depth'}) 34 | var idmap = {} 35 | 36 | return { 37 | addRoute: addRoute, 38 | route: route, 39 | tearDown: tearDown, 40 | print: print, 41 | transports: transports 42 | } 43 | 44 | function addRoute (pattern, tf) { 45 | assert(tf, 'addRoute requires a valid handler or transport function') 46 | assert(tf.type && (tf.type === 'handler' || tf.type === 'transport' || tf.type === 'callback'), 'addRoute requires a known type') 47 | 48 | idmap[tf.muid] = tf 49 | 50 | if (!pattern) return 51 | 52 | if (typeof pattern !== 'string') { 53 | run.add(pattern, tf) 54 | return 55 | } 56 | 57 | if (pattern === '*') { 58 | logger.debug('adding default route') 59 | run.default(tf) 60 | } 61 | } 62 | 63 | /** 64 | * main routing function 65 | */ 66 | function route (message, cb) { 67 | assert(message, 'route requries a valid message') 68 | assert(cb && (typeof cb === 'function'), 'route requires a valid callback handler') 69 | 70 | if (message.pattern) { 71 | return pattern(message, cb) 72 | } 73 | 74 | if (message.response) { 75 | return response(message, cb) 76 | } 77 | 78 | malformed(message, cb) 79 | } 80 | 81 | function tearDown (cb) { 82 | var list = run.list().map(function (el) { 83 | return el.tearDown 84 | }).filter(Boolean) 85 | 86 | if (!list.length) { 87 | if (cb) cb() 88 | return 89 | } 90 | if (cb) { 91 | parallel(null, list, null, cb) 92 | return 93 | } 94 | 95 | list.forEach(function (tearDown) { 96 | tearDown(cb) 97 | }) 98 | } 99 | 100 | function transports () { 101 | return run.list() 102 | } 103 | 104 | function print () { 105 | var result = '' 106 | 107 | result += 'patterns:\n' 108 | run.list(null, { payloads: true, patterns: true }).forEach(function (el) { 109 | if (el.default) { 110 | result += '*' 111 | } else { 112 | result += stringify(el.pattern) 113 | } 114 | result += ' : ' + el.payload.muid + ' (' + el.payload.type + ')' + '\n' 115 | }) 116 | return result 117 | } 118 | 119 | function pattern (message, cb) { 120 | 121 | // we are routing an outbound message as there is a pattern attached to the message 122 | var match = run.lookup(message.pattern) 123 | 124 | if (!match) { 125 | 126 | // unable to find a route, discard message 127 | logger.error(NO_AVAILABLE_TRANSPORT) 128 | logger.debug(message, 'discarded message') 129 | cb(mue.transport(NO_AVAILABLE_TRANSPORT, message)) 130 | return 131 | } 132 | 133 | assert(match.type, 'handler', 'pattern message type must be handler or transport') 134 | assert(match.type, 'transport', 'pattern message type must be handler or transport') 135 | 136 | // message will be handled in this process instance. In this case just reflect the error and 137 | // response parameters back to the callback handler. This will either be local in the case of a single 138 | // process instance or more likely in transport.js. This will pack the error and response paramters into 139 | // a protocol packet send 140 | if (match.type === 'handler') { 141 | logger.debug(message, 'handling message') 142 | match.tf(message.pattern, function (err, response) { 143 | cb(mue.wrap(err || null), response || {}, message) 144 | }, message) 145 | 146 | return 147 | } 148 | 149 | // update the response routing information 150 | var muid = message.protocol.path[message.protocol.path.length - 1] 151 | if (!idmap[muid] && message.protocol.inboundIfc) { 152 | idmap[muid] = idmap[message.protocol.inboundIfc] 153 | } 154 | 155 | if (match.direction !== 'outbound') { 156 | logger.error(INVALID_OUTBOUND_ROUTE) 157 | cb(mue.transport(INVALID_OUTBOUND_ROUTE)) 158 | return 159 | } 160 | 161 | // message will be sent to this transport handler. In this case the error paramter signals an internal error 162 | // condition within the transport handler, for example a socket timeout. In this instance the error message will 163 | // be logged and an exception thrown. This will result in this node crashing and restarting - this is by design 164 | match.tf(message, function (err) { 165 | if (err) { 166 | cb(mue.wrap(err)) 167 | } 168 | }) 169 | } 170 | 171 | function response (message, cb) { 172 | // we are routing a response message as there is a response block on the message and no pattern block 173 | assert(message.protocol, 'response message must have a protocol object') 174 | 175 | // pull the last muid in the chain 176 | var muid = message.protocol.path[message.protocol.path.length - 1] 177 | var match = idmap[muid] 178 | if (!match) { 179 | 180 | // there is no available transport or handler for this mu id, this should never happen, discard the packet... 181 | logger.error(NO_MATCHING_ROUTE) 182 | cb(mue.framework(NO_MATCHING_ROUTE, message)) 183 | return 184 | } 185 | 186 | // we have a matching muid check if the response handler is in this instance of mu, it it is call the callback handler 187 | // this will be the last step in the distributed call chain. Otherwise the message is being routed through a transport layer 188 | // so call the tf and invoke the local callback once the message has been sent 189 | if (match.type === 'callback') { 190 | match.tf(mue.remote(message.err || null), message.response, message) 191 | return 192 | } 193 | 194 | match.tf(message, function (err, response) { 195 | cb(mue.wrap(err), response) 196 | }) 197 | } 198 | 199 | function malformed (message, cb) { 200 | logger.error(MALFORMED_PACKET) 201 | logger.debug(message, 'malformed message') 202 | cb(mue.transport(MALFORMED_PACKET, message)) 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /packages/mu-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-router", 3 | "version": "1.0.6", 4 | "description": "The official pattern router for mu", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tap test" 8 | }, 9 | "keywords": [ 10 | "mu", 11 | "microservices", 12 | "messaging", 13 | "router", 14 | "distributed" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/apparatus/mu.git" 19 | }, 20 | "author": "Peter Elger (https://github.com/pelger)", 21 | "contributors": [ 22 | "David Mark Clements (https://github.com/davidmarkclements)", 23 | "Dean McDonnell (https://github.com/mcdonnelldean)", 24 | "Matteo Collina (https://github.com/mcollina)" 25 | ], 26 | "license": "MIT", 27 | "devDependencies": { 28 | "abstract-logging": "^1.0.0", 29 | "mu-error": "^1.4.5", 30 | "tap": "^8.0.0" 31 | }, 32 | "dependencies": { 33 | "bloomrun": "^3.0.0", 34 | "fast-safe-stringify": "^1.1.1", 35 | "fastparallel": "^2.3.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/mu-router/test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tap').test 4 | var logger = require('abstract-logging') 5 | var mue = require('mu-error')() 6 | var router = require('../') 7 | 8 | var MALFORMED_PACKET = 'Malformed packet no pattern or response field. Message will be discarded' 9 | var NO_MATCHING_ROUTE = 'Routing error: no matching route and no default route provided, Message will be discarded' 10 | 11 | test('malformed packet', function (t) { 12 | var api = router({logger: logger, mue: mue}) 13 | 14 | api.route({}, function (err) { 15 | t.ok(err instanceof Error) 16 | t.is(err.isBoom, true) 17 | t.is(err.isMu, true) 18 | err = mue.extract(err) 19 | t.is(err.code, mue.ERRORS.TRANSPORT) 20 | t.is(err.message, MALFORMED_PACKET) 21 | t.end() 22 | }) 23 | }) 24 | 25 | test('no matching route', function (t) { 26 | var api = router({logger: logger, mue: mue}) 27 | 28 | api.route({response: {}, protocol: {path: ['test']}}, function (err) { 29 | t.ok(err instanceof Error) 30 | t.is(err.isBoom, true) 31 | t.is(err.isMu, true) 32 | err = mue.extract(err) 33 | t.is(err.code, mue.ERRORS.FRAMEWORK) 34 | t.is(err.message, NO_MATCHING_ROUTE) 35 | t.end() 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /packages/mu-tcp/README.md: -------------------------------------------------------------------------------- 1 | # mu-tcp 2 | 3 | The official TCP transport driver for mu 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-tcp` is a TCP transport driver for `mu`, it allows for message dispatching across HTTP. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-tcp 27 | ``` 28 | 29 | ## API 30 | To do.. 31 | 32 | ## License 33 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 34 | 35 | 36 | [mu]: https://github.com/apparatus/mu 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-tcp 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu-tcp/driver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var net = require('net') 18 | var nos = require('net-object-stream') 19 | var eos = require('end-of-stream') 20 | var through = require('through2') 21 | var stringify = require('fast-safe-stringify') 22 | var Parse = require('fast-json-parse') 23 | var pump = require('pump') 24 | var assert = require('assert') 25 | var mue = require('mu-error')() 26 | 27 | /** 28 | * TCP transport. Simple protocol JSON messages delinated by two byte signature and length field 29 | * [sig][len][JSON][sig][len][JSON]... 30 | */ 31 | module.exports = function createTcpDriver (options) { 32 | return function tcpDriver (opts, receive) { 33 | var connections = {} 34 | var connectionsByIp = {} 35 | 36 | assert(opts, 'transport should always pass opts to tcpDriver') 37 | assert(receive instanceof Function, 'transport should always pass receive function to tcpDriver') 38 | 39 | var server = options.source && 40 | listen(options.source.port, options.source.host, options.ready) 41 | 42 | if (!server && options.ready instanceof Function) { 43 | options.ready() 44 | } 45 | 46 | return { 47 | type: 'tcp', 48 | send: send, 49 | tearDown: tearDown 50 | } 51 | 52 | function send (message, cb) { 53 | var err = null 54 | if (!connections[message.protocol.dst]) { 55 | var socket = net.createConnection(options.target.port, options.target.host) 56 | connections[message.protocol.dst] = nos(socket, {codec: codec(socket)}) 57 | connections[message.protocol.dst].socket = socket 58 | connections[message.protocol.dst].on('data', function (data) { 59 | if (data instanceof Error) { 60 | receive(data) 61 | socket.end() 62 | return 63 | } 64 | receive(null, data) 65 | }) 66 | 67 | socket.on('error', function (err) { receive(mue.transport(err)) }) 68 | socket.once('error', function (error) { 69 | err = error 70 | }) 71 | eos(connections[message.protocol.dst], function () { 72 | connections[message.protocol.dst] = null 73 | }) 74 | } 75 | 76 | connections[message.protocol.dst].write(message, cb && function () { 77 | setImmediate(function () { cb(err) }) 78 | }) 79 | } 80 | 81 | function listen (port, host, ready) { 82 | return net.createServer(function (socket) { 83 | socket = nos(socket, {codec: codec(socket)}) 84 | 85 | var inbound = through.obj(function (data, _, cb) { 86 | if (data instanceof Error) { 87 | cb(data) 88 | return 89 | } 90 | 91 | if (!connections[data.protocol.src]) { 92 | connections[data.protocol.src] = socket 93 | connectionsByIp[socket.remoteAddress + '_' + socket.remotePort] = data.protocol.src 94 | } 95 | 96 | receive(null, data) 97 | cb() 98 | }) 99 | 100 | pump(socket, inbound, socket, function (err) { 101 | connections[connectionsByIp[socket.remoteAddress + '_' + socket.remotePort]] = null 102 | connectionsByIp[socket.remoteAddress + '_' + socket.remotePort] = null 103 | if (err) { receive(mue.transport(err)) } 104 | }) 105 | }).listen(port, host, ready) 106 | } 107 | 108 | function codec () { 109 | return { 110 | encode: stringify, 111 | decode: function decode (data) { 112 | var result = new Parse(data) 113 | if (result.err) { 114 | return mue.transport(result.err) 115 | } 116 | return result.value 117 | } 118 | } 119 | } 120 | 121 | function tearDown (cb) { 122 | for (var conn in connections) { 123 | if (connections[conn]) { 124 | connections[conn].end() 125 | } 126 | } 127 | if (!server && cb) { 128 | cb() 129 | return 130 | } 131 | if (server) { 132 | if (cb) { 133 | setImmediate(function () { 134 | server.close(cb) 135 | }) 136 | return 137 | } 138 | server.close() 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /packages/mu-tcp/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var transport = require('mu-transport') 18 | var tcpDriver = require('./driver') 19 | 20 | module.exports = { 21 | server: function server (source, ready) { 22 | return function driver (mu, opts) { 23 | var drv = tcpDriver({source: source, id: opts.id, ready: ready}) 24 | return transport(drv, mu, opts) 25 | } 26 | }, 27 | client: function client (target, ready) { 28 | return function driver (mu, opts) { 29 | var drv = tcpDriver({target: target, id: opts.id, ready: ready}) 30 | return transport(drv, mu, opts) 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /packages/mu-tcp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-tcp", 3 | "version": "1.0.9", 4 | "description": "The official TCP transport driver for mu", 5 | "main": "index.js", 6 | "keywords": [ 7 | "tcp", 8 | "mu", 9 | "transport", 10 | "microservices", 11 | "messaging", 12 | "router", 13 | "distributed" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/apparatus/mu.git" 18 | }, 19 | "author": "Peter Elger (https://github.com/pelger)", 20 | "contributors": [ 21 | "David Mark Clements (https://github.com/davidmarkclements)", 22 | "Dean McDonnell (https://github.com/mcdonnelldean)", 23 | "Matteo Collina (https://github.com/mcollina)" 24 | ], 25 | "license": "MIT", 26 | "dependencies": { 27 | "end-of-stream": "^1.1.0", 28 | "fast-json-parse": "^1.0.2", 29 | "fast-safe-stringify": "^1.1.1", 30 | "mu-error": "^1.4.5", 31 | "mu-transport": "^1.0.9", 32 | "net-object-stream": "^2.0.0", 33 | "pump": "^1.0.1", 34 | "through2": "^2.0.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/mu-tee/README.md: -------------------------------------------------------------------------------- 1 | # mu-tee 2 | 3 | An official transport adapter for mu that sends each request to every supplied transport 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-tee` is a transport adapter that sends each request to every supplied transport. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-tee 27 | ``` 28 | 29 | ## API 30 | To do.. 31 | 32 | ## License 33 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 34 | 35 | 36 | [mu]: https://github.com/apparatus/mu 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-tee 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu-tee/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var assert = require('assert') 18 | var uuid = require('uuid') 19 | var cloneDeep = require('lodash.clonedeep') 20 | 21 | /** 22 | * Tee transport adapter. Sends request to each supplied transport 23 | */ 24 | module.exports = function tee (transports) { 25 | return function adapter (mu, opts) { 26 | assert(opts) 27 | var direction = opts.direction 28 | var muid = opts.id || uuid() 29 | 30 | transports = transports.map(function (t) { 31 | return t(mu, {direction: direction, id: muid}) 32 | }) 33 | 34 | return { 35 | tf: tf, 36 | muid: muid, 37 | direction: transports[0].direction, 38 | type: 'transport', 39 | tearDown: tearDown 40 | } 41 | 42 | function tf (message, cb) { 43 | for (var index = 0; index < transports.length; ++index) { 44 | transports[index].tf(cloneDeep(message), cb) 45 | } 46 | } 47 | 48 | function tearDown (cb) { 49 | var count = 0 50 | transports.forEach(function (transport) { 51 | transport.tearDown(function () { 52 | ++count 53 | if (count === transports.length) { 54 | cb && cb() 55 | } 56 | }) 57 | }) 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /packages/mu-tee/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-tee", 3 | "version": "1.0.5", 4 | "description": "An official transport adapter for mu that sends each request to every supplied transport", 5 | "main": "index.js", 6 | "keywords": [ 7 | "tee", 8 | "mu", 9 | "adapter", 10 | "microservices", 11 | "messaging", 12 | "router", 13 | "distributed" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/apparatus/mu.git" 18 | }, 19 | "author": "Peter Elger (https://github.com/pelger)", 20 | "contributors": [ 21 | "David Mark Clements (https://github.com/davidmarkclements)", 22 | "Dean McDonnell (https://github.com/mcdonnelldean)", 23 | "Matteo Collina (https://github.com/mcollina)" 24 | ], 25 | "license": "MIT", 26 | "dependencies": { 27 | "lodash.clonedeep": "^4.5.0", 28 | "uuid": "^3.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/mu-transport/README.md: -------------------------------------------------------------------------------- 1 | # mu-transport 2 | 3 | The official protocol implementation and transport driver factory used internally by mu 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Part of the Official [mu][Mu Suite]. 14 | 15 | `mu-transport` is the protocol implementation and transport driver factory used internally by [mu][`mu`], it takes a transport driver and wraps messages in protocol meta info. 16 | 17 | 18 | * [Install](#install) 19 | * [API](#api) 20 | * [License](#license) 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | $ npm install mu-transport 27 | ``` 28 | 29 | ## API 30 | To do.. 31 | 32 | ## License 33 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 34 | 35 | 36 | [mu]: https://github.com/apparatus/mu 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu-transport 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu-transport/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var assert = require('assert') 18 | var uuid = require('uuid') 19 | var cloneDeep = require('lodash.clonedeep') 20 | 21 | 22 | /** 23 | * responsible for the protocol implementation 24 | * {pattern: { pattern and data }, proto: { path: [1234, 4567], trace: [1234, 4567], ttl: 9}} 25 | * {response: { response data }, proto: { path: [1234], dst: 4567, trace: [1234, 4567, 6789], ttl: 8}} 26 | */ 27 | module.exports = function transport (createDriver, mu, opts) { 28 | assert(opts, 'transport opts should always be defined') 29 | var direction = opts.direction 30 | var muid = opts.id || uuid() 31 | var logger = mu.log.child({muid: muid}) 32 | var driver = createDriver({id: muid}, receive) 33 | 34 | return { 35 | muid: muid, 36 | tf: tf, 37 | direction: direction, 38 | type: 'transport', 39 | driver: driver, 40 | tearDown: driver.tearDown 41 | } 42 | 43 | 44 | 45 | function constructFailResponse (err, msg) { 46 | var pkt = { err: err, 47 | response: {}, 48 | protocol: {}} 49 | var elt = muid 50 | 51 | if (msg.protocol && msg.protocol.path && msg.protocol.path.length && msg.protocol.path.length > 0) { 52 | elt = msg.protocol.path[msg.protocol.path.length - 1] 53 | pkt.protocol.path = msg.protocol.path.slice(0, msg.protocol.path.length - 1) 54 | } 55 | 56 | if (msg.protocol && msg.protocol.trace) { 57 | pkt.protocol.trace = cloneDeep(msg.protocol.trace) 58 | } 59 | 60 | pkt.protocol.dst = elt 61 | pkt.protocol.src = elt 62 | pkt.protocol.inboundIfc = elt 63 | return pkt 64 | } 65 | 66 | 67 | 68 | function receive (err, msg) { 69 | logger.debug({in: msg}, 'message received') 70 | if (err) { 71 | 72 | // received an error condition from the driver, typically this signals a failed client connection 73 | // or other inbound connection error condition. In this case, log the error but make no attempt at 74 | // further routing unless we have an associated msg - the associated message will be used to construct 75 | // an error response if present 76 | logger.error(err) 77 | if (!msg || !msg.protocol) { 78 | return 79 | } else { 80 | msg = constructFailResponse(err, msg) 81 | } 82 | } 83 | 84 | msg.protocol.inboundIfc = muid 85 | mu.dispatch(msg, function (err, response) { 86 | var message = cloneDeep(msg) 87 | response = response || {} 88 | var packet = { 89 | err: err, 90 | response: cloneDeep(response), 91 | protocol: message.protocol 92 | } 93 | packet.protocol.trace.push(muid) 94 | packet.protocol.src = muid 95 | packet.protocol.dst = packet.protocol.path.pop() 96 | logger.debug({out: packet}, 'sending response') 97 | 98 | driver.send(packet, function (err) { 99 | if (err) { 100 | logger.error({ err: err, out: packet }) 101 | } 102 | }) 103 | }) 104 | } 105 | 106 | 107 | 108 | function tf (msg, cb) { 109 | assert(msg) 110 | assert(msg.protocol) 111 | assert(cb && (typeof cb === 'function'), 112 | 'transport requires a valid callback handler') 113 | 114 | var message = cloneDeep(msg) 115 | 116 | if (message.pattern) { 117 | message.protocol.path.push(muid) 118 | } 119 | if (!message.protocol.dst) { 120 | message.protocol.dst = 'target' 121 | } 122 | if (message.response) { 123 | message.protocol.dst = message.protocol.path.pop() 124 | } 125 | 126 | message.protocol.src = muid 127 | message.protocol.trace.push(muid) 128 | 129 | logger.debug({out: message}, 'sending via transport driver') 130 | driver.send(message, cb) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /packages/mu-transport/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu-transport", 3 | "version": "1.0.9", 4 | "description": "The official protocol implementation and transport driver factory used internally by mu", 5 | "main": "index.js", 6 | "keywords": [ 7 | "mu", 8 | "transport", 9 | "microservices", 10 | "messaging", 11 | "router", 12 | "distributed" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/apparatus/mu.git" 17 | }, 18 | "author": "Peter Elger (https://github.com/pelger)", 19 | "contributors": [ 20 | "David Mark Clements (https://github.com/davidmarkclements)", 21 | "Dean McDonnell (https://github.com/mcdonnelldean)", 22 | "Matteo Collina (https://github.com/mcollina)" 23 | ], 24 | "license": "MIT", 25 | "dependencies": { 26 | "lodash.clonedeep": "^4.5.0", 27 | "uuid": "^3.0.0" 28 | }, 29 | "devDependencies": { 30 | "abstract-logging": "^1.0.0", 31 | "mu": "^2.1.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/mu-transport/test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tap').test 4 | var createMu = require('mu') 5 | var transport = require('../') 6 | var createLogger = () => Object.assign({}, require('abstract-logging'), {child: function () { return this }}) 7 | 8 | test('driver send error', function (t) { 9 | var logger = createLogger() 10 | var mu = createMu({logger: logger}) 11 | var testErr = Error('test') 12 | 13 | transport(createDriver, mu, {}) 14 | 15 | function createDriver (drv, receive) { 16 | process.nextTick(() => { 17 | logger.error = function (o) { 18 | if (!o.err) return 19 | t.is(o.err, testErr) 20 | t.ok(o.out) 21 | t.end() 22 | } 23 | receive(null, {protocol: {trace: [], path: []}, pattern: {test: 'test'}}) 24 | }) 25 | return { 26 | send: (p, cb) => cb(testErr) 27 | } 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /packages/mu/README.md: -------------------------------------------------------------------------------- 1 | # mu 2 | 3 | A message-pattern based router for building distributed systems 4 | 5 | [![npm][npm-badge]][npm-url] 6 | [![travis][travis-badge]][travis-url] 7 | [![coveralls][coveralls-badge]][coveralls-url] 8 | [![david][david-badge]][david-url] 9 | 10 | - __Sponsor:__ [nearForm][sponsor] 11 | - __Status:__ Experimental 12 | 13 | Mu is a message based router for building distributed systems. It allows messages to be routed and 14 | handled across various transports. Mu is aggressively light weight to ensure ease of use and speed 15 | of execution. 16 | 17 | * [Install](#install) 18 | * [API](#api) 19 | * [License](#license) 20 | 21 | 22 | ## Install 23 | To install mu, simply use npm, 24 | 25 | ```sh 26 | $ npm install mu 27 | ``` 28 | 29 | ## API 30 | 31 | To do.. 32 | 33 | ## License 34 | Copyright Peter Elger 2016 & Contributors, Licensed under [MIT][]. 35 | 36 | 37 | [travis-badge]: https://travis-ci.org/apparatus/mu.svg?branch=master 38 | [travis-url]: https://travis-ci.org/apparatus/mu 39 | [npm-badge]: https://badge.fury.io/js/mu.svg 40 | [npm-url]: https://npmjs.org/package/mu 41 | [logo-url]: https://raw.githubusercontent.com/apparatus/mu/master/assets/mu.png 42 | [coveralls-badge]: https://coveralls.io/repos/apparatus/mu/badge.svg?branch=master 43 | [coveralls-url]: https://coveralls.io/github/apparatus/mu?branch=master 44 | [david-badge]: https://david-dm.org/apparatus/mu.svg 45 | [david-url]: https://david-dm.org/apparatus/mu?path=packages/mu 46 | [sponsor]: http://nearform.com 47 | [MIT]: ./LICENSE 48 | -------------------------------------------------------------------------------- /packages/mu/adapters/balancer.js: -------------------------------------------------------------------------------- 1 | throw Error('mu/drivers/balancer does not exist, you want http://npm.im/mu-balancer') 2 | -------------------------------------------------------------------------------- /packages/mu/adapters/tee.js: -------------------------------------------------------------------------------- 1 | throw Error('mu/drivers/tee does not exist, you want http://npm.im/mu-tee') 2 | -------------------------------------------------------------------------------- /packages/mu/drivers/func.js: -------------------------------------------------------------------------------- 1 | throw Error('mu/drivers/func does not exist, you want http://npm.im/mu-local') 2 | -------------------------------------------------------------------------------- /packages/mu/drivers/redis.js: -------------------------------------------------------------------------------- 1 | throw Error('mu/drivers/redis does not exist, you want http://npm.im/mu-redis') 2 | -------------------------------------------------------------------------------- /packages/mu/drivers/tcp.js: -------------------------------------------------------------------------------- 1 | throw Error('mu/drivers/tcp does not exist, you want http://npm.im/mu-tcp') 2 | -------------------------------------------------------------------------------- /packages/mu/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var assert = require('assert') 18 | var uuid = require('uuid') 19 | var pino = require('pino') 20 | var muError = require('mu-error') 21 | var createRouter = require('mu-router') 22 | var DEFAULT_TTL = 10 23 | 24 | function createMu (opts) { 25 | opts = opts || {} 26 | // see mu-error for error options 27 | opts.errors = opts.errors || {} 28 | var logger = opts.logger || pino() 29 | var dev = opts.dev 30 | var mue = muError(Object.assign({ 31 | dev: dev 32 | }, opts.errors)) 33 | var router = createRouter({logger: logger, mue: mue}) 34 | 35 | if (opts.logLevel) { 36 | logger.level = opts.logLevel 37 | } 38 | 39 | var instance = { 40 | inbound: inbound, 41 | outbound: outbound, 42 | define: define, 43 | dispatch: dispatch, 44 | tearDown: router.tearDown, 45 | print: router.print, 46 | log: logger, 47 | error: mue, 48 | DEV_MODE: dev, 49 | transports: router.transports 50 | } 51 | 52 | return instance 53 | 54 | function define (pattern, tf) { 55 | assert(pattern, 'define requires a valid pattern') 56 | assert(tf, 'define requires a valid transport or pattern handler') 57 | logger.debug('adding pattern route: ' + pattern) 58 | 59 | if (typeof tf === 'function') { 60 | router.addRoute(pattern, {muid: uuid(), tf: tf, type: 'handler'}) 61 | } else { 62 | router.addRoute(pattern, tf) 63 | } 64 | } 65 | 66 | function inbound (pattern, tf) { 67 | define(pattern, tf(instance, {direction: 'inbound'})) 68 | } 69 | 70 | function outbound (pattern, tf) { 71 | define(pattern, tf(instance, {direction: 'outbound'})) 72 | } 73 | 74 | function dispatch (message, cb) { 75 | var id = uuid() 76 | cb = cb || noop 77 | assert(message, 'dispatch requires a valid message') 78 | assert(typeof cb === 'function', 'dispatch requires a valid callback handler') 79 | logger.debug('dispatching message: ' + message) 80 | 81 | if (!(message.pattern || message.response)) { 82 | router.addRoute(null, {muid: id, tf: cb, type: 'callback'}) 83 | router.route({pattern: message, protocol: {path: [id], trace: [id], ttl: DEFAULT_TTL}}, cb) 84 | return 85 | } 86 | 87 | router.route(message, cb) 88 | } 89 | } 90 | 91 | function noop () {} 92 | 93 | createMu.log = Object.keys(pino.levels.values).reduce((acc, key) => { 94 | acc['level' + key[0].toUpperCase() + key.slice(1)] = key 95 | return acc 96 | }, {}) 97 | 98 | module.exports = createMu 99 | -------------------------------------------------------------------------------- /packages/mu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mu", 3 | "version": "2.1.4", 4 | "description": "A message-pattern based router for building distributed systems", 5 | "main": "index.js", 6 | "keywords": [ 7 | "microservices", 8 | "mu", 9 | "messaging", 10 | "router", 11 | "distributed" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/apparatus/mu.git" 16 | }, 17 | "author": "Peter Elger (https://github.com/pelger)", 18 | "contributors": [ 19 | "David Mark Clements (https://github.com/davidmarkclements)", 20 | "Dean McDonnell (https://github.com/mcdonnelldean)", 21 | "Matteo Collina (https://github.com/mcollina)" 22 | ], 23 | "license": "MIT", 24 | "dependencies": { 25 | "bloomrun": "^3.0.0", 26 | "fast-safe-stringify": "^1.1.3", 27 | "mu-error": "^1.4.5", 28 | "mu-router": "^1.0.6", 29 | "mu-transport": "^1.0.9", 30 | "mu-local": "^1.0.9", 31 | "mu-tcp": "^1.0.9", 32 | "mu-http": "^1.0.5", 33 | "mu-redis": "^1.0.9", 34 | "mu-dns": "^1.0.5", 35 | "mu-balance": "^1.0.5", 36 | "mu-tee": "^1.0.5", 37 | "pino": "^3.0.1", 38 | "uuid": "^3.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/adapters/balancer.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var mu = require('../../packages/mu')() 19 | var tcp = require('../../packages/mu-tcp') 20 | var balance = require('../../packages/mu-balance') 21 | var service = require('../system/service1/service') 22 | 23 | function init (cb) { 24 | service(function (s1) { 25 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 26 | service(function (s2) { 27 | s2.inbound('*', tcp.server({port: 3002, host: '127.0.0.1'})) 28 | cb(s1, s2) 29 | }) 30 | }) 31 | } 32 | 33 | test('consume services with tcp balancer adapter', function (t) { 34 | init(function (s1, s2) { 35 | mu.outbound({role: 's1'}, balance([tcp.client({port: 3001, host: '127.0.0.1'}), 36 | tcp.client({port: 3002, host: '127.0.0.1'})])) 37 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 38 | t.equal(null, err) 39 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 40 | t.equal(null, err) 41 | mu.tearDown() 42 | s1.tearDown() 43 | s2.tearDown() 44 | t.end() 45 | }) 46 | }) 47 | }) 48 | }) 49 | 50 | -------------------------------------------------------------------------------- /test/adapters/tee.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var mu = require('../../packages/mu')() 19 | var tcp = require('../../packages/mu-tcp') 20 | var tee = require('../../packages/mu-tee') 21 | var service = require('../system/service1/service') 22 | 23 | function init (cb) { 24 | service(function (s1) { 25 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 26 | service(function (s2) { 27 | s2.inbound('*', tcp.server({port: 3002, host: '127.0.0.1'})) 28 | cb(s1, s2) 29 | }) 30 | }) 31 | } 32 | 33 | test('consume services with tcp tee adapter', function (t) { 34 | t.plan(6) 35 | 36 | init(function (s1, s2) { 37 | mu.outbound({role: 's1'}, tee([tcp.client({port: 3001, host: '127.0.0.1'}), 38 | tcp.client({port: 3002, host: '127.0.0.1'})])) 39 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 40 | t.equal(null, err) 41 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 42 | t.equal(null, err) 43 | mu.tearDown(function () {}) 44 | s1.tearDown() 45 | s2.tearDown() 46 | }) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/core/basic.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var createMu = require('../../packages/mu') 19 | var local = require('../../packages/mu-local') 20 | 21 | 22 | test('local handler test', function (t) { 23 | t.plan(6) 24 | 25 | var mu = createMu({ logLevel: createMu.log.levelInfo }) 26 | 27 | mu.define({role: 'test', cmd: 'one'}, function (args, cb) { 28 | t.deepEqual(args, { role: 'test', cmd: 'one', fish: 'cheese' }, 'check pattern cmd one') 29 | cb() 30 | }) 31 | 32 | mu.define({role: 'test', cmd: 'two'}, function (args, cb) { 33 | t.deepEqual(args, { role: 'test', cmd: 'two', fish: 'cheese' }, 'check pattern cmd two') 34 | cb(null, {my: 'response'}) 35 | }) 36 | 37 | mu.dispatch({role: 'test', cmd: 'one', fish: 'cheese'}, function (err, result) { 38 | t.equal(null, err, 'check err is null') 39 | t.deepEqual({}, result, 'check result is empty') 40 | }) 41 | 42 | mu.dispatch({role: 'test', cmd: 'two', fish: 'cheese'}, function (err, result) { 43 | t.equal(null, err, 'check err is null') 44 | t.deepEqual({my: 'response'}, result, 'check result') 45 | }) 46 | }) 47 | 48 | 49 | test('full message test', function (t) { 50 | t.plan(9) 51 | 52 | var mu = createMu({ logLevel: createMu.log.levelInfo }) 53 | 54 | mu.define({role: 'test', cmd: 'one'}, function (args, cb) { 55 | t.deepEqual(args, { role: 'test', cmd: 'one', fish: 'cheese' }, 'check pattern cmd one') 56 | cb() 57 | }) 58 | 59 | mu.define({role: 'test', cmd: 'two'}, function (args, cb, msg) { 60 | t.deepEqual(args, { role: 'test', cmd: 'two', fish: 'cheese' }, 'check pattern cmd two') 61 | t.ok(msg.protocol) 62 | cb(null, {my: 'response'}) 63 | }) 64 | 65 | mu.dispatch({role: 'test', cmd: 'one', fish: 'cheese'}, function (err, result, msg) { 66 | t.equal(null, err, 'check err is null') 67 | t.deepEqual({}, result, 'check result is empty') 68 | t.ok(msg.protocol) 69 | }) 70 | 71 | mu.dispatch({role: 'test', cmd: 'two', fish: 'cheese'}, function (err, result, msg) { 72 | t.equal(null, err, 'check err is null') 73 | t.deepEqual({my: 'response'}, result, 'check result') 74 | t.ok(msg.protocol) 75 | }) 76 | }) 77 | 78 | test('route print test', function (t) { 79 | t.plan(2) 80 | 81 | var mu = createMu() 82 | mu.inbound('*', local()) 83 | 84 | mu.define({role: 'test', cmd: 'one'}, function (args, cb) { 85 | t.deepEqual(args, { role: 'test', cmd: 'one', fish: 'cheese' }, 'check pattern cmd one') 86 | cb() 87 | }) 88 | 89 | mu.define({role: 'test', cmd: 'two'}, function (args, cb) { 90 | t.deepEqual(args, { role: 'test', cmd: 'two', fish: 'cheese' }, 'check pattern cms two') 91 | cb(null, {my: 'response'}) 92 | }) 93 | 94 | var routing = mu.print() 95 | t.notEqual(routing.indexOf('{"role":"test","cmd":"one"}'), -1) 96 | t.notEqual(routing.indexOf('{"role":"test","cmd":"two"}'), -1) 97 | }) 98 | 99 | test('multi-dispatch same cb', function (t) { 100 | t.plan(18) 101 | 102 | var mu = createMu({ logLevel: createMu.log.levelInfo }) 103 | var count = 0 104 | var total = 0 105 | mu.define({role: 'test', cmd: 'one'}, function (args, cb) { 106 | cb(null, {count: ++count, id: args.id}) 107 | }) 108 | 109 | function handler (err, result) { 110 | t.equal(null, err, 'check err is null') 111 | t.equal(result.count, ++total, 'check count is ' + total) 112 | t.equal(result.count, result.id, 'check count is ' + result.id) 113 | } 114 | 115 | mu.dispatch({role: 'test', cmd: 'one', id: 1}, handler) 116 | mu.dispatch({role: 'test', cmd: 'one', id: 2}, handler) 117 | mu.dispatch({role: 'test', cmd: 'one', id: 3}, handler) 118 | mu.dispatch({role: 'test', cmd: 'one', id: 4}, handler) 119 | mu.dispatch({role: 'test', cmd: 'one', id: 5}, handler) 120 | mu.dispatch({role: 'test', cmd: 'one', id: 6}, handler) 121 | }) 122 | 123 | test('optional dispatch cb', function (t) { 124 | t.plan(2) 125 | 126 | var mu = createMu() 127 | 128 | mu.define({role: 'test', cmd: 'one'}, function (args, cb) { 129 | t.pass() 130 | cb() 131 | }) 132 | 133 | t.doesNotThrow(function () { 134 | mu.dispatch({role: 'test', cmd: 'one'}) 135 | }) 136 | }) 137 | 138 | test('tearDown cb', function (t) { 139 | t.plan(1) 140 | createMu().tearDown(function () { 141 | t.pass() 142 | }) 143 | }) 144 | 145 | test('bogus string route', function (t) { 146 | t.plan(1) 147 | var mu = createMu() 148 | 149 | t.doesNotThrow(function () { 150 | mu.define('test', function (args, cb) { 151 | t.fail() 152 | // nothing should happen here 153 | }) 154 | }) 155 | }) 156 | 157 | -------------------------------------------------------------------------------- /test/core/legacy.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | 19 | test('removed APIs with useful message', function (t) { 20 | t.throws(function () { 21 | require('../../packages/mu/drivers/func') 22 | }, 'mu/drivers/func does not exist, you want http://npm.im/mu-local') 23 | t.throws(function () { 24 | require('../../packages/mu/drivers/tcp') 25 | }, 'mu/drivers/tcp does not exist, you want http://npm.im/mu-tcp') 26 | t.throws(function () { 27 | require('../../packages/mu/drivers/redis') 28 | }, 'mu/drivers/redis does not exist, you want http://npm.im/mu-redis') 29 | t.throws(function () { 30 | require('../../packages/mu/adapters/tee') 31 | }, 'mu/adapters/tee does not exist, you want http://npm.im/mu-tee') 32 | t.throws(function () { 33 | require('../../packages/mu/adapters/balancer') 34 | }, 'mu/adapters/balancer does not exist, you want http://npm.im/mu-balancer') 35 | t.end() 36 | }) 37 | -------------------------------------------------------------------------------- /test/core/transport.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var createMu = require('../../packages/mu') 19 | var local = require('../../packages/mu-local') 20 | 21 | test('local inbound/outbound test', function (t) { 22 | t.plan(6) 23 | 24 | // service 25 | var mus = createMu() 26 | mus.define({role: 'test', cmd: 'one'}, function (args, cb) { 27 | t.deepEqual(args, { role: 'test', cmd: 'one', fish: 'cheese' }, 'check pattern cmd one') 28 | cb() 29 | }) 30 | 31 | mus.define({role: 'test', cmd: 'two'}, function (args, cb) { 32 | t.deepEqual(args, { role: 'test', cmd: 'two', fish: 'cheese' }, 'check pattern cmd two') 33 | cb(null, {my: 'response'}) 34 | }) 35 | mus.inbound('*', local()) 36 | 37 | // consumer 38 | var mu = createMu() 39 | mu.outbound('*', local({target: mus})) 40 | 41 | // execute 42 | mu.dispatch({role: 'test', cmd: 'one', fish: 'cheese'}, function (err, result) { 43 | t.equal(null, err, 'check err is null') 44 | t.deepEqual({}, result, 'check response is empty') 45 | }) 46 | 47 | mu.dispatch({role: 'test', cmd: 'two', fish: 'cheese'}, function (err, result) { 48 | t.equal(null, err, 'check err is null') 49 | t.deepEqual({my: 'response'}, result, 'check result') 50 | }) 51 | }) 52 | 53 | test('tearDown cb with local transport', function (t) { 54 | t.plan(2) 55 | 56 | // service 57 | var mus = createMu() 58 | mus.inbound('*', local()) 59 | 60 | mus.tearDown(function () { 61 | t.pass() 62 | }) 63 | 64 | // consumer 65 | var mu = createMu() 66 | mu.outbound('*', local({target: mus})) 67 | 68 | mu.tearDown(function () { 69 | t.pass() 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /test/drivers/http-driver/500error.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var http = require('http') 18 | var test = require('tap').test 19 | var muHttp = require('../../../packages/mu-http') 20 | var Mu = require('../../../packages/mu') 21 | var service3 = require('../../system/service3/service') 22 | 23 | 24 | function initFailServer (port, host, cb) { 25 | var server = http.createServer() 26 | 27 | server.on('request', function (request, response) { 28 | var body = [] 29 | 30 | request.on('data', function (chunk) { 31 | body.push(chunk) 32 | }) 33 | 34 | request.on('end', function () { 35 | response.writeHead(500, { 'Content-Type': 'application/json' }) 36 | response.write(JSON.stringify({result: 'error'})) 37 | response.end() 38 | server.close() 39 | }) 40 | }) 41 | server.listen(port, host, cb) 42 | } 43 | 44 | 45 | function initService3 (cb) { 46 | service3(function (s3) { 47 | s3.inbound('*', muHttp.server({port: 3003, host: '127.0.0.1'})) 48 | s3.outbound({role: 's1'}, muHttp.client({port: 3001, host: '127.0.0.1'})) 49 | cb(s3) 50 | }) 51 | } 52 | 53 | 54 | function initRouting (cb) { 55 | var router = Mu() 56 | router.inbound('*', muHttp.server({port: 3003, host: '127.0.0.1'})) 57 | router.outbound({role: 's1'}, muHttp.client({port: 3001, host: '127.0.0.1'})) 58 | cb(router) 59 | } 60 | 61 | 62 | test('force http protcol error single', function (t) { 63 | t.plan(1) 64 | var mu = Mu() 65 | 66 | mu.outbound('*', muHttp.client({port: 3001, host: '127.0.0.1'})) 67 | 68 | initFailServer(3001, '127.0.0.1', function () { 69 | mu.dispatch({role: 'test', cmd: 'one', fish: 'cheese'}, function (err, result) { 70 | t.equal(err.output.statusCode, 500) 71 | }) 72 | }) 73 | }) 74 | 75 | 76 | test('force http protcol error chained', function (t) { 77 | t.plan(1) 78 | var mu = Mu() 79 | 80 | mu.outbound('*', muHttp.client({port: 3003, host: '127.0.0.1'})) 81 | 82 | initService3(function (s3) { 83 | initFailServer(3001, '127.0.0.1', function () { 84 | mu.dispatch({role: 's3', cmd: 'one'}, function (err, result) { 85 | t.equal(err.output.statusCode, 500) 86 | s3.tearDown() 87 | }) 88 | }) 89 | }) 90 | }) 91 | 92 | 93 | test('force http protcol error routed', function (t) { 94 | t.plan(1) 95 | var mu = Mu() 96 | 97 | mu.outbound('*', muHttp.client({port: 3003, host: '127.0.0.1'})) 98 | 99 | initRouting(function (router) { 100 | initFailServer(3001, '127.0.0.1', function () { 101 | mu.dispatch({role: 's1', cmd: 'one'}, function (err, result) { 102 | t.equal(err.output.statusCode, 500) 103 | router.tearDown() 104 | }) 105 | }) 106 | }) 107 | }) 108 | 109 | -------------------------------------------------------------------------------- /test/drivers/http-driver/error.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var createMu = require('../../../packages/mu') 19 | var mu = createMu() 20 | var http = require('../../../packages/mu-http') 21 | var net = require('net') 22 | var errorService = require('../../system/errorService/service') 23 | 24 | function init (cb) { 25 | errorService(function (errSvc) { 26 | errSvc.inbound('*', http.server({port: 3001, host: '127.0.0.1'})) 27 | cb(errSvc) 28 | }) 29 | } 30 | 31 | test('test no service', function (t) { 32 | mu.outbound('*', http.client({port: 3001, host: '127.0.0.1'})) 33 | mu.dispatch({role: 'wibble', cmd: 'fish'}, function (err, result) { 34 | mu.tearDown() 35 | t.ok(err instanceof Error) 36 | t.is(err.isBoom, true) 37 | t.is(err.isMu, true) 38 | t.is(err.code, 'ECONNREFUSED', 'check connection refused') 39 | t.end() 40 | }) 41 | }) 42 | 43 | test('test mangled data received by server', function (t) { 44 | var server = createMu() 45 | var childLogger = server.log.child({}) 46 | server.log.child = function () { 47 | return Object.assign(childLogger, { 48 | error: function (err) { 49 | t.ok(err, 'error sent to logger') 50 | t.ok(err instanceof Error, 'is instance of Error') 51 | t.ok(err instanceof SyntaxError, 'is instance of SyntaxError') 52 | t.is(err.message.slice(0, 16), 'Unexpected token', 'message is unexpected token') 53 | } 54 | }) 55 | } 56 | 57 | server.inbound('*', http.server({port: 3001, host: '127.0.0.1'})) 58 | var socket = net.connect(3001, '127.0.0.1') 59 | socket.write(Buffer.from('99027c227061747465726e223a7b226869223a22796f75227d2c2270726f746f636f6c223a7b2270617468223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c227472616365223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c2274746c223a31302c22647374223a22746172676574222c22737263223a2237393930396339302d303739392d346361302d396530612d623636356663303231623932227d7d', 'hex')) 60 | socket.on('close', function () { 61 | t.pass('server closed socket') 62 | server.tearDown() 63 | t.end() 64 | }) 65 | }) 66 | 67 | test('test mangled data received by client', function (t) { 68 | var server = net.createServer(function (socket) { 69 | socket.write(Buffer.from('99027c227061747465726e223a7b226869223a22796f75227d2c2270726f746f636f6c223a7b2270617468223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c227472616365223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c2274746c223a31302c22647374223a22746172676574222c22737263223a2237393930396339302d303739392d346361302d396530612d623636356663303231623932227d7d', 'hex')) 70 | socket.on('close', function () { 71 | t.pass('client closed socket') 72 | client.tearDown() 73 | server.close() 74 | t.end() 75 | }) 76 | }).listen(3001) 77 | 78 | var client = createMu() 79 | var childLogger = client.log.child({}) 80 | client.log.child = function () { 81 | return Object.assign(childLogger, { 82 | error: function (err) { 83 | t.ok(err, 'error sent to logger') 84 | t.ok(err instanceof Error, 'is instance of Error') 85 | t.ok(err instanceof SyntaxError, 'is instance of SyntaxError') 86 | t.is(err.message.slice(0, 16), 'Unexpected token', 'message is unexpected token') 87 | } 88 | }) 89 | } 90 | client.outbound('*', http.client({port: 3001, host: '127.0.0.1'})) 91 | client.dispatch({hi: 'you'}, function (err) { 92 | console.log(err) 93 | }) 94 | }) 95 | 96 | test('test match nothing', function (t) { 97 | init(function (errSvc) { 98 | mu.outbound('*', http.client({port: 3001, host: '127.0.0.1'})) 99 | mu.dispatch({role: 'wibble', cmd: 'fish'}, function (err, result) { 100 | mu.tearDown() 101 | t.ok(err instanceof Error) 102 | t.is(err.isBoom, true) 103 | t.is(err.isMu, true) 104 | err = mu.error.extract(err) 105 | t.is(err.code, mu.error.ERRORS.TRANSPORT) 106 | t.is(err.message, 'Routing error: no valid outbound route available. Message will be discarded') 107 | errSvc.tearDown() 108 | t.end() 109 | }) 110 | }) 111 | }) 112 | 113 | test('test match partial', function (t) { 114 | init(function (errSvc) { 115 | mu.outbound('*', http.client({port: 3001, host: '127.0.0.1'})) 116 | mu.dispatch({role: 'error', cmd: 'fish'}, function (err, result) { 117 | mu.tearDown() 118 | t.ok(err instanceof Error) 119 | t.is(err.isBoom, true) 120 | t.is(err.isMu, true) 121 | err = mu.error.extract(err) 122 | t.is(err.code, mu.error.ERRORS.TRANSPORT) 123 | t.is(err.message, 'Routing error: no valid outbound route available. Message will be discarded') 124 | errSvc.tearDown() 125 | t.end() 126 | }) 127 | }) 128 | }) 129 | 130 | test('service returning inbound error', function (t) { 131 | init(function (errSvc) { 132 | mu.outbound('*', http.client({port: 3001, host: '127.0.0.1'})) 133 | mu.dispatch({role: 'error', cmd: 'error'}, function (err, result) { 134 | mu.tearDown() 135 | t.ok(err instanceof Error) 136 | t.is(err.isBoom, true) 137 | t.is(err.isMu, true) 138 | err = mu.error.extract(err) 139 | t.is(err.code, mu.error.ERRORS.SERVICE) 140 | t.is(err.message, 'oh fek', 'check error response') 141 | errSvc.tearDown() 142 | t.end() 143 | }) 144 | }) 145 | }) 146 | 147 | -------------------------------------------------------------------------------- /test/drivers/http-driver/http.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var http = require('../../../packages/mu-http') 19 | var service1 = require('../../system/service1/service') 20 | var createConsumer = require('../../system/consumer/consumer') 21 | var createMu = require('../../../packages/mu') 22 | 23 | function init (cb) { 24 | service1(function (s1) { 25 | s1.inbound('*', http.server({port: 3001, host: '127.0.0.1'})) 26 | require('../../system/service2/service')(function (s2) { 27 | s2.inbound('*', http.server({port: 3002, host: '127.0.0.1'})) 28 | cb(s1, s2) 29 | }) 30 | }) 31 | } 32 | 33 | test('consume services with http transport test', function (t) { 34 | t.plan(1) 35 | 36 | init(function (s1, s2) { 37 | var consumer = createConsumer() 38 | consumer.mu.outbound({role: 's1'}, http.client({port: 3001, host: '127.0.0.1'})) 39 | consumer.mu.outbound({role: 's2'}, http.client({port: 3002, host: '127.0.0.1'})) 40 | consumer.consume(function (err, result) { 41 | t.equal(err, null, 'check err is null') 42 | consumer.mu.tearDown() 43 | s1.tearDown() 44 | s2.tearDown() 45 | }) 46 | }) 47 | }) 48 | 49 | test('multi-dispatch same cb', function (t) { 50 | t.plan(18) 51 | 52 | var mu1 = createMu() 53 | var mu2 = createMu() 54 | mu1.inbound('*', http.server({port: 3003, host: '127.0.0.1'})) 55 | mu2.outbound({role: 'multi-dispatch-test'}, http.client({port: 3003, host: '127.0.0.1'})) 56 | 57 | var count = 0 58 | var total = 0 59 | mu1.define({role: 'multi-dispatch-test', cmd: 'one'}, function (args, cb) { 60 | cb(null, {count: ++count, id: args.id}) 61 | }) 62 | 63 | function handler (err, result) { 64 | t.equal(null, err, 'check err is null') 65 | t.equal(result.count, ++total, 'check count is ' + total) 66 | t.equal(result.count, result.id, 'check count is ' + result.id) 67 | 68 | if (total === 6) { 69 | mu1.tearDown() 70 | mu2.tearDown() 71 | } 72 | } 73 | 74 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 1}, handler) 75 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 2}, handler) 76 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 3}, handler) 77 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 4}, handler) 78 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 5}, handler) 79 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 6}, handler) 80 | }) 81 | 82 | test('server/client ready cbs', function (t) { 83 | t.plan(2) 84 | var mu = createMu() 85 | mu.inbound('*', http.server({port: 3003, host: '127.0.0.1'}, function () { 86 | t.pass() 87 | mu.tearDown() 88 | })) 89 | 90 | mu.outbound({some: 'pattern'}, http.client({port: 3003, host: '127.0.0.1'}, function () { 91 | t.pass() 92 | })) 93 | }) 94 | 95 | test('tearDown cb', function (t) { 96 | t.plan(2) 97 | 98 | var mu1 = createMu() 99 | var mu2 = createMu() 100 | mu1.inbound('*', http.server({port: 3003, host: '127.0.0.1'})) 101 | mu2.inbound('*', http.server({port: 3004, host: '127.0.0.1'})) 102 | mu2.outbound({role: 'test'}, http.client({port: 3003, host: '127.0.0.1'})) 103 | 104 | mu1.tearDown(function () { 105 | t.pass() 106 | }) 107 | 108 | mu2.tearDown(function () { 109 | t.pass() 110 | }) 111 | }) 112 | -------------------------------------------------------------------------------- /test/drivers/http-driver/multi-response.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var http = require('../../../packages/mu-http') 19 | var rcc = require('../../system/consumer/responseCountConsumer')(1) 20 | var zrs = require('../../system/zeroResponseService/service') 21 | var mrs = require('../../system/multiResponseService/service') 22 | 23 | function initHttp (cb) { 24 | zrs(function (zero) { 25 | zero.inbound('*', http.server({port: 3001, host: '127.0.0.1'})) 26 | mrs(function (multi) { 27 | multi.inbound('*', http.server({port: 3002, host: '127.0.0.1'})) 28 | cb(zero, multi) 29 | }) 30 | }) 31 | } 32 | 33 | test('consume services expect two responses', function (t) { 34 | t.plan(1) 35 | var count = 0 36 | 37 | initHttp(function (zero, multi) { 38 | rcc.mu.outbound({role: 'multi'}, http.client({port: 3002, host: '127.0.0.1'})) 39 | rcc.consumeMulti(function (countResult) { 40 | count = countResult 41 | }) 42 | setTimeout(function () { 43 | t.equal(count, 1, 'check service returns one response') 44 | multi.tearDown() 45 | zero.tearDown() 46 | rcc.mu.tearDown() 47 | }, 500) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/drivers/http-driver/routing-errors.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var createMu = require('../../../packages/mu') 19 | var http = require('../../../packages/mu-http') 20 | var NO_AVAILABLE_TRANSPORT = 'Routing error: no available transport function' 21 | 22 | var s1 = createMu() 23 | 24 | function h1 (args, cb) { 25 | cb() 26 | } 27 | 28 | s1.define({role: 's1', cmd: 'one'}, h1) 29 | s1.inbound({role: 's1'}, http.server({port: 3001, host: '127.0.0.1'})) 30 | 31 | test('test no service', function (t) { 32 | t.plan(2) 33 | 34 | var mu = createMu() 35 | mu.outbound({role: 's1'}, http.client({port: 3001, host: '127.0.0.1'})) 36 | mu.dispatch({role: 's1', cmd: 'one'}, function (err, result) { 37 | t.equal(err, null) 38 | mu.dispatch({wibble: 'fish'}, function (err, result) { 39 | t.equal(err.message, NO_AVAILABLE_TRANSPORT) 40 | s1.tearDown() 41 | mu.tearDown() 42 | }) 43 | }) 44 | }) 45 | 46 | -------------------------------------------------------------------------------- /test/drivers/http-driver/service-chain.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var http = require('../../../packages/mu-http') 19 | var mu = require('../../../packages/mu')() 20 | var service1 = require('../../system/service1/service') 21 | var service2 = require('../../system/service2/service') 22 | var service3 = require('../../system/service3/service') 23 | 24 | function init (cb) { 25 | service1(function (s1) { 26 | s1.inbound('*', http.server({port: 3001, host: '127.0.0.1'})) 27 | service2(function (s2) { 28 | s2.inbound('*', http.server({port: 3002, host: '127.0.0.1'})) 29 | service3(function (s3) { 30 | s3.inbound('*', http.server({port: 3003, host: '127.0.0.1'})) 31 | s3.outbound({role: 's1'}, http.client({port: 3001, host: '127.0.0.1'})) 32 | cb(s1, s2, s3) 33 | }) 34 | }) 35 | }) 36 | } 37 | 38 | test('consume services with http transport test', function (t) { 39 | t.plan(2) 40 | 41 | init(function (s1, s2, s3) { 42 | mu.outbound({role: 's1'}, http.client({port: 3001, host: '127.0.0.1'})) 43 | mu.outbound({role: 's2'}, http.client({port: 3002, host: '127.0.0.1'})) 44 | mu.outbound({role: 's3'}, http.client({port: 3003, host: '127.0.0.1'})) 45 | mu.dispatch({role: 's3', cmd: 'one'}, function (err, result) { 46 | t.equal(null, err) 47 | t.deepEqual({my: 'response'}, result) 48 | mu.tearDown() 49 | s1.tearDown() 50 | s2.tearDown() 51 | s3.tearDown() 52 | }) 53 | }) 54 | }) 55 | 56 | -------------------------------------------------------------------------------- /test/drivers/http-driver/service-routing.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var http = require('../../../packages/mu-http') 19 | var createMu = require('../../../packages/mu') 20 | var service1 = require('../../system/service1/service') 21 | 22 | function init (cb) { 23 | service1(function (s1) { 24 | s1.inbound('*', http.server({port: 3001, host: '127.0.0.1'})) 25 | var router = createMu() 26 | router.inbound('*', http.server({port: 3003, host: '127.0.0.1'})) 27 | router.outbound({role: 's1'}, http.client({port: 3001, host: '127.0.0.1'})) 28 | cb(s1, router) 29 | }) 30 | } 31 | 32 | // TODO: also add test {role: 's2'} should fail no outbound route 33 | 34 | test('consume services with http transport test', function (t) { 35 | t.plan(2) 36 | 37 | init(function (s1, router) { 38 | var client = createMu() 39 | client.outbound({role: 's1'}, http.client({port: 3003, host: '127.0.0.1'})) 40 | client.dispatch({role: 's1', cmd: 'two'}, function (err, result) { 41 | t.equal(null, err) 42 | t.deepEqual({my: 'response'}, result) 43 | client.tearDown() 44 | s1.tearDown() 45 | router.tearDown() 46 | }) 47 | }) 48 | }) 49 | 50 | -------------------------------------------------------------------------------- /test/drivers/http-driver/zero-response.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var http = require('../../../packages/mu-http') 19 | var rcc = require('../../system/consumer/responseCountConsumer')() 20 | var zrs = require('../../system/zeroResponseService/service') 21 | 22 | function initHttp (cb) { 23 | zrs(function (zero) { 24 | zero.inbound('*', http.server({port: 3001, host: '127.0.0.1'})) 25 | require('../../system/multiResponseService/service')(function (multi) { 26 | multi.inbound('*', http.server({port: 3002, host: '127.0.0.1'})) 27 | cb(zero, multi) 28 | }) 29 | }) 30 | } 31 | 32 | test('consume services expect no response', function (t) { 33 | t.plan(1) 34 | 35 | initHttp(function (zero, multi) { 36 | rcc.mu.outbound({role: 'zero'}, http.client({port: 3001, host: '127.0.0.1'})) 37 | rcc.consumeZero(function (result) { 38 | t.equal(true, result) 39 | multi.tearDown() 40 | zero.tearDown() 41 | rcc.mu.tearDown() 42 | }) 43 | }) 44 | }) 45 | 46 | -------------------------------------------------------------------------------- /test/drivers/local-driver/force-driver-error.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var createMu = require('../../../packages/mu') 19 | var local = require('../../../packages/mu-local') 20 | 21 | test('force an error with function transport test for coverage numbers', function (t) { 22 | t.plan(1) 23 | 24 | var mu1 = createMu() 25 | 26 | mu1.define({role: 's1', cmd: 'one'}, function (args, cb) { 27 | cb() 28 | }) 29 | 30 | mu1.define({role: 's1', cmd: 'two'}, function (args, cb) { 31 | cb(null, {my: 'response'}) 32 | }) 33 | 34 | mu1.inbound('*', local()) 35 | 36 | var mu = createMu() 37 | 38 | mu.outbound({role: 's1'}, local({target: mu1})) 39 | 40 | mu.dispatch({role: 's1', cmd: 'one', __err: 'err'}, function () { 41 | }) 42 | setTimeout(function () { 43 | mu1.tearDown() 44 | mu.tearDown() 45 | t.pass('expect no response from driver fail') 46 | }, 500) 47 | }) 48 | 49 | -------------------------------------------------------------------------------- /test/drivers/local-driver/local.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var local = require('../../../packages/mu-local') 19 | var service1 = require('../../system/service1/service') 20 | var service2 = require('../../system/service2/service') 21 | var createConsumer = require('../../system/consumer/consumer') 22 | 23 | function init (cb) { 24 | service1(function (s1) { 25 | s1.inbound('*', local()) 26 | service2(function (s2) { 27 | s2.inbound('*', local()) 28 | cb(s1, s2) 29 | }) 30 | }) 31 | } 32 | 33 | test('consume services with function transport test', function (t) { 34 | t.plan(2) 35 | 36 | init(function (s1, s2) { 37 | var consumer = createConsumer() 38 | consumer.mu.outbound({role: 's1'}, local({target: s1})) 39 | consumer.mu.outbound({role: 's2'}, local({target: s2})) 40 | 41 | consumer.consume(function (err, result) { 42 | t.equal(err, null, 'check err is null') 43 | t.deepEqual(result, {my: 'response'}, 'check result') 44 | consumer.mu.tearDown() 45 | s1.tearDown() 46 | s2.tearDown() 47 | }) 48 | }) 49 | }) 50 | 51 | -------------------------------------------------------------------------------- /test/drivers/redis-driver/redis.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var proxyquire = require('proxyquire') 19 | var redisMock = require('fakeredis') 20 | var driver = proxyquire('../../../packages/mu-redis/driver', {redis: redisMock}) 21 | var rdis = proxyquire('../../../packages/mu-redis', {driver: driver}) 22 | 23 | // write separate redis test 2 senders 1 reciever sendres to send 100x each and ensure that each gets back exactly 24 | // correct response -> count and validate 25 | 26 | function init (cb) { 27 | require('../../system/service1/service')(function (s1) { 28 | s1.inbound('*', rdis.server({port: 6379, host: '127.0.0.1', list: 's1'})) 29 | require('../../system/service2/service')(function (s2) { 30 | s2.inbound('*', rdis.server({port: 6379, host: '127.0.0.1', list: 's2'})) 31 | cb(s1, s2) 32 | }) 33 | }) 34 | } 35 | 36 | 37 | test('consume services with redis transport test', function (t) { 38 | t.plan(1) 39 | 40 | init(function (s1, s2) { 41 | var consumer = require('../../system/consumer/consumer')() 42 | consumer.mu.outbound({role: 's1'}, rdis.client({port: 6379, host: '127.0.0.1', list: 's1'})) 43 | consumer.mu.outbound({role: 's2'}, rdis.client({port: 6379, host: '127.0.0.1', list: 's2'})) 44 | consumer.consume(function (err, result) { 45 | t.equal(err, null, 'check err is null') 46 | consumer.mu.tearDown() 47 | s1.tearDown() 48 | s2.tearDown() 49 | }) 50 | }) 51 | }) 52 | 53 | -------------------------------------------------------------------------------- /test/drivers/tcp-driver/error.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var createMu = require('../../../packages/mu') 19 | var mu = createMu() 20 | var tcp = require('../../../packages/mu-tcp') 21 | var net = require('net') 22 | var errorService = require('../../system/errorService/service') 23 | 24 | function init (cb) { 25 | errorService(function (errSvc) { 26 | errSvc.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 27 | cb(errSvc) 28 | }) 29 | } 30 | 31 | test('test no service', function (t) { 32 | mu.outbound('*', tcp.client({port: 3001, host: '127.0.0.1'})) 33 | mu.dispatch({role: 'wibble', cmd: 'fish'}, function (err, result) { 34 | mu.tearDown() 35 | t.ok(err instanceof Error) 36 | t.is(err.isBoom, true) 37 | t.is(err.isMu, true) 38 | t.is(err.code, 'ECONNREFUSED', 'check connection refused') 39 | t.end() 40 | }) 41 | }) 42 | 43 | test('test mangled data received by server', function (t) { 44 | var server = createMu() 45 | var childLogger = server.log.child({}) 46 | server.log.child = function () { 47 | return Object.assign(childLogger, { 48 | error: function (err) { 49 | t.ok(err, 'error sent to logger') 50 | t.ok(err instanceof Error, 'is instance of Error') 51 | t.ok(err instanceof SyntaxError, 'is instance of SyntaxError') 52 | t.is(err.message.slice(0, 16), 'Unexpected token', 'message is unexpected token') 53 | } 54 | }) 55 | } 56 | 57 | server.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 58 | var socket = net.connect(3001, '127.0.0.1') 59 | socket.write(Buffer.from('99027c227061747465726e223a7b226869223a22796f75227d2c2270726f746f636f6c223a7b2270617468223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c227472616365223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c2274746c223a31302c22647374223a22746172676574222c22737263223a2237393930396339302d303739392d346361302d396530612d623636356663303231623932227d7d', 'hex')) 60 | socket.on('close', function () { 61 | t.pass('server closed socket') 62 | server.tearDown() 63 | t.end() 64 | }) 65 | }) 66 | 67 | test('test mangled data received by client', function (t) { 68 | var server = net.createServer(function (socket) { 69 | socket.write(Buffer.from('99027c227061747465726e223a7b226869223a22796f75227d2c2270726f746f636f6c223a7b2270617468223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c227472616365223a5b2261336137666336312d343661652d343462352d626535352d613731343639646336393139222c2237393930396339302d303739392d346361302d396530612d623636356663303231623932225d2c2274746c223a31302c22647374223a22746172676574222c22737263223a2237393930396339302d303739392d346361302d396530612d623636356663303231623932227d7d', 'hex')) 70 | socket.on('close', function () { 71 | t.pass('client closed socket') 72 | client.tearDown() 73 | server.close() 74 | t.end() 75 | }) 76 | }).listen(3001) 77 | 78 | var client = createMu() 79 | var childLogger = client.log.child({}) 80 | client.log.child = function () { 81 | return Object.assign(childLogger, { 82 | error: function (err) { 83 | t.ok(err, 'error sent to logger') 84 | t.ok(err instanceof Error, 'is instance of Error') 85 | t.ok(err instanceof SyntaxError, 'is instance of SyntaxError') 86 | t.is(err.message.slice(0, 16), 'Unexpected token', 'message is unexpected token') 87 | } 88 | }) 89 | } 90 | client.outbound('*', tcp.client({port: 3001, host: '127.0.0.1'})) 91 | client.dispatch({hi: 'you'}, function (err) { 92 | console.log(err) 93 | }) 94 | }) 95 | 96 | test('test match nothing', function (t) { 97 | init(function (errSvc) { 98 | mu.outbound('*', tcp.client({port: 3001, host: '127.0.0.1'})) 99 | mu.dispatch({role: 'wibble', cmd: 'fish'}, function (err, result) { 100 | mu.tearDown() 101 | t.ok(err instanceof Error) 102 | t.is(err.isBoom, true) 103 | t.is(err.isMu, true) 104 | err = mu.error.extract(err) 105 | t.is(err.code, mu.error.ERRORS.TRANSPORT) 106 | t.is(err.message, 'Routing error: no valid outbound route available. Message will be discarded') 107 | errSvc.tearDown() 108 | t.end() 109 | }) 110 | }) 111 | }) 112 | 113 | test('test match partial', function (t) { 114 | init(function (errSvc) { 115 | mu.outbound('*', tcp.client({port: 3001, host: '127.0.0.1'})) 116 | mu.dispatch({role: 'error', cmd: 'fish'}, function (err, result) { 117 | mu.tearDown() 118 | t.ok(err instanceof Error) 119 | t.is(err.isBoom, true) 120 | t.is(err.isMu, true) 121 | err = mu.error.extract(err) 122 | t.is(err.code, mu.error.ERRORS.TRANSPORT) 123 | t.is(err.message, 'Routing error: no valid outbound route available. Message will be discarded') 124 | errSvc.tearDown() 125 | t.end() 126 | }) 127 | }) 128 | }) 129 | 130 | test('service returning inbound error', function (t) { 131 | init(function (errSvc) { 132 | mu.outbound('*', tcp.client({port: 3001, host: '127.0.0.1'})) 133 | mu.dispatch({role: 'error', cmd: 'error'}, function (err, result) { 134 | mu.tearDown() 135 | t.ok(err instanceof Error) 136 | t.is(err.isBoom, true) 137 | t.is(err.isMu, true) 138 | err = mu.error.extract(err) 139 | t.is(err.code, mu.error.ERRORS.SERVICE) 140 | t.is(err.message, 'oh fek', 'check error response') 141 | errSvc.tearDown() 142 | t.end() 143 | }) 144 | }) 145 | }) 146 | 147 | -------------------------------------------------------------------------------- /test/drivers/tcp-driver/multi-response,test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var tcp = require('../../../packages/mu-tcp') 19 | var rcc = require('../../system/consumer/responseCountConsumer')(2) 20 | var zrs = require('../../system/zeroResponseService/service') 21 | var mrs = require('../../system/multiResponseService/service') 22 | 23 | function initTcp (cb) { 24 | zrs(function (zero) { 25 | zero.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 26 | mrs(function (multi) { 27 | multi.inbound('*', tcp.server({port: 3002, host: '127.0.0.1'})) 28 | cb(zero, multi) 29 | }) 30 | }) 31 | } 32 | 33 | test('consume services expect two responses', function (t) { 34 | t.plan(1) 35 | var count = 0 36 | 37 | initTcp(function (zero, multi) { 38 | rcc.mu.outbound({role: 'multi'}, tcp.client({port: 3002, host: '127.0.0.1'})) 39 | rcc.consumeMulti(function (countResult) { 40 | count = countResult 41 | }) 42 | setTimeout(function () { 43 | t.equal(count, 2, 'check service returns two responses') 44 | multi.tearDown() 45 | zero.tearDown() 46 | rcc.mu.tearDown() 47 | }, 500) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/drivers/tcp-driver/routing-errors.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var createMu = require('../../../packages/mu') 19 | var tcp = require('../../../packages/mu-tcp') 20 | var NO_AVAILABLE_TRANSPORT = 'Routing error: no available transport function' 21 | 22 | var s1 = createMu() 23 | 24 | function h1 (args, cb) { 25 | cb() 26 | } 27 | 28 | s1.define({role: 's1', cmd: 'one'}, h1) 29 | s1.inbound({role: 's1'}, tcp.server({port: 3001, host: '127.0.0.1'})) 30 | 31 | test('test no service', function (t) { 32 | t.plan(2) 33 | 34 | var mu = createMu() 35 | mu.outbound({role: 's1'}, tcp.client({port: 3001, host: '127.0.0.1'})) 36 | mu.dispatch({role: 's1', cmd: 'one'}, function (err, result) { 37 | t.equal(err, null) 38 | mu.dispatch({wibble: 'fish'}, function (err, result) { 39 | t.equal(err.message, NO_AVAILABLE_TRANSPORT) 40 | s1.tearDown() 41 | mu.tearDown() 42 | }) 43 | }) 44 | }) 45 | 46 | -------------------------------------------------------------------------------- /test/drivers/tcp-driver/service-chain.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var tcp = require('../../../packages/mu-tcp') 19 | var mu = require('../../../packages/mu')() 20 | var service1 = require('../../system/service1/service') 21 | var service2 = require('../../system/service2/service') 22 | var service3 = require('../../system/service3/service') 23 | 24 | function init (cb) { 25 | service1(function (s1) { 26 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 27 | service2(function (s2) { 28 | s2.inbound('*', tcp.server({port: 3002, host: '127.0.0.1'})) 29 | service3(function (s3) { 30 | s3.inbound('*', tcp.server({port: 3003, host: '127.0.0.1'})) 31 | s3.outbound({role: 's1'}, tcp.client({port: 3001, host: '127.0.0.1'})) 32 | cb(s1, s2, s3) 33 | }) 34 | }) 35 | }) 36 | } 37 | 38 | test('consume services with tcp transport test', function (t) { 39 | t.plan(2) 40 | 41 | init(function (s1, s2, s3) { 42 | mu.outbound({role: 's1'}, tcp.client({port: 3001, host: '127.0.0.1'})) 43 | mu.outbound({role: 's2'}, tcp.client({port: 3002, host: '127.0.0.1'})) 44 | mu.outbound({role: 's3'}, tcp.client({port: 3003, host: '127.0.0.1'})) 45 | mu.dispatch({role: 's3', cmd: 'one'}, function (err, result) { 46 | t.equal(null, err) 47 | t.deepEqual({my: 'response'}, result) 48 | mu.tearDown() 49 | s1.tearDown() 50 | s2.tearDown() 51 | s3.tearDown() 52 | }) 53 | }) 54 | }) 55 | 56 | -------------------------------------------------------------------------------- /test/drivers/tcp-driver/service-routing.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var tcp = require('../../../packages/mu-tcp') 19 | var createMu = require('../../../packages/mu') 20 | var service1 = require('../../system/service1/service') 21 | 22 | function init (cb) { 23 | service1(function (s1) { 24 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 25 | var router = createMu() 26 | router.inbound('*', tcp.server({port: 3003, host: '127.0.0.1'})) 27 | router.outbound({role: 's1'}, tcp.client({port: 3001, host: '127.0.0.1'})) 28 | cb(s1, router) 29 | }) 30 | } 31 | 32 | // TODO: also add test {role: 's2'} should fail no outbound route 33 | 34 | test('consume services with tcp transport test', function (t) { 35 | t.plan(2) 36 | 37 | init(function (s1, router) { 38 | var client = createMu() 39 | client.outbound({role: 's1'}, tcp.client({port: 3003, host: '127.0.0.1'})) 40 | client.dispatch({role: 's1', cmd: 'two'}, function (err, result) { 41 | t.equal(null, err) 42 | t.deepEqual({my: 'response'}, result) 43 | client.tearDown() 44 | s1.tearDown() 45 | router.tearDown() 46 | }) 47 | }) 48 | }) 49 | 50 | -------------------------------------------------------------------------------- /test/drivers/tcp-driver/tcp.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var tcp = require('../../../packages/mu-tcp') 19 | var service1 = require('../../system/service1/service') 20 | var createConsumer = require('../../system/consumer/consumer') 21 | var createMu = require('../../../packages/mu') 22 | 23 | function init (cb) { 24 | service1(function (s1) { 25 | s1.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 26 | require('../../system/service2/service')(function (s2) { 27 | s2.inbound('*', tcp.server({port: 3002, host: '127.0.0.1'})) 28 | cb(s1, s2) 29 | }) 30 | }) 31 | } 32 | 33 | test('consume services with tcp transport test', function (t) { 34 | t.plan(1) 35 | 36 | init(function (s1, s2) { 37 | var consumer = createConsumer() 38 | consumer.mu.outbound({role: 's1'}, tcp.client({port: 3001, host: '127.0.0.1'})) 39 | consumer.mu.outbound({role: 's2'}, tcp.client({port: 3002, host: '127.0.0.1'})) 40 | consumer.consume(function (err, result) { 41 | t.equal(err, null, 'check err is null') 42 | consumer.mu.tearDown() 43 | s1.tearDown() 44 | s2.tearDown() 45 | }) 46 | }) 47 | }) 48 | 49 | test('multi-dispatch same cb', function (t) { 50 | t.plan(18) 51 | 52 | var mu1 = createMu() 53 | var mu2 = createMu() 54 | mu1.inbound('*', tcp.server({port: 3003, host: '127.0.0.1'})) 55 | mu2.outbound({role: 'multi-dispatch-test'}, tcp.client({port: 3003, host: '127.0.0.1'})) 56 | 57 | var count = 0 58 | var total = 0 59 | mu1.define({role: 'multi-dispatch-test', cmd: 'one'}, function (args, cb) { 60 | cb(null, {count: ++count, id: args.id}) 61 | }) 62 | 63 | function handler (err, result) { 64 | t.equal(null, err, 'check err is null') 65 | t.equal(result.count, ++total, 'check count is ' + total) 66 | t.equal(result.count, result.id, 'check count is ' + result.id) 67 | 68 | if (total === 6) { 69 | mu1.tearDown() 70 | mu2.tearDown() 71 | } 72 | } 73 | 74 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 1}, handler) 75 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 2}, handler) 76 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 3}, handler) 77 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 4}, handler) 78 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 5}, handler) 79 | mu2.dispatch({role: 'multi-dispatch-test', cmd: 'one', id: 6}, handler) 80 | }) 81 | 82 | test('server/client ready cbs', function (t) { 83 | t.plan(2) 84 | var mu = createMu() 85 | mu.inbound('*', tcp.server({port: 3003, host: '127.0.0.1'}, function () { 86 | t.pass() 87 | mu.tearDown() 88 | })) 89 | 90 | mu.outbound({some: 'pattern'}, tcp.client({port: 3003, host: '127.0.0.1'}, function () { 91 | t.pass() 92 | })) 93 | }) 94 | 95 | test('tearDown cb', function (t) { 96 | t.plan(2) 97 | 98 | var mu1 = createMu() 99 | var mu2 = createMu() 100 | mu1.inbound('*', tcp.server({port: 3003, host: '127.0.0.1'})) 101 | mu2.inbound('*', tcp.server({port: 3004, host: '127.0.0.1'})) 102 | mu2.outbound({role: 'test'}, tcp.client({port: 3003, host: '127.0.0.1'})) 103 | 104 | mu1.tearDown(function () { 105 | t.pass() 106 | }) 107 | 108 | mu2.tearDown(function () { 109 | t.pass() 110 | }) 111 | }) 112 | 113 | -------------------------------------------------------------------------------- /test/drivers/tcp-driver/zero-response.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var test = require('tap').test 18 | var tcp = require('../../../packages/mu-tcp') 19 | var rcc = require('../../system/consumer/responseCountConsumer')() 20 | var zrs = require('../../system/zeroResponseService/service') 21 | 22 | function initTcp (cb) { 23 | zrs(function (zero) { 24 | zero.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 25 | require('../../system/multiResponseService/service')(function (multi) { 26 | multi.inbound('*', tcp.server({port: 3002, host: '127.0.0.1'})) 27 | cb(zero, multi) 28 | }) 29 | }) 30 | } 31 | 32 | test('consume services expect no response', function (t) { 33 | t.plan(1) 34 | 35 | initTcp(function (zero, multi) { 36 | rcc.mu.outbound({role: 'zero'}, tcp.client({port: 3001, host: '127.0.0.1'})) 37 | rcc.consumeZero(function (result) { 38 | t.equal(true, result) 39 | multi.tearDown() 40 | zero.tearDown() 41 | rcc.mu.tearDown() 42 | }) 43 | }) 44 | }) 45 | 46 | -------------------------------------------------------------------------------- /test/system/consumer/consumer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var mu = require('../../../packages/mu')() 18 | 19 | module.exports = function () { 20 | function consume (cb) { 21 | mu.dispatch({role: 's2', cmd: 'one', fish: 'cheese'}, function (err, result) { 22 | if (err) { console.log(err) } 23 | mu.dispatch({role: 's1', cmd: 'two', fish: 'cheese'}, function (err, result) { 24 | if (err) { console.log(err) } 25 | cb(err, result) 26 | }) 27 | }) 28 | } 29 | 30 | return { 31 | mu: mu, 32 | consume: consume 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /test/system/consumer/responseCountConsumer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var mu = require('../../../packages/mu')() 18 | 19 | module.exports = function (expectCount) { 20 | function consumeZero (cb) { 21 | mu.dispatch({role: 'zero', cmd: 'one'}, function () { 22 | cb(false) 23 | }) 24 | setTimeout(function () { 25 | cb(true) 26 | }, 1000) 27 | } 28 | 29 | function consumeMulti (cb) { 30 | var count = 0 31 | mu.dispatch({role: 'multi', cmd: 'one'}, function () { 32 | count = count + 1 33 | if (count === expectCount) { 34 | cb(count) 35 | } 36 | }) 37 | } 38 | 39 | return { 40 | mu: mu, 41 | consumeZero: consumeZero, 42 | consumeMulti: consumeMulti 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /test/system/errorService/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var tcp = require('../../../packages/mu-tcp') 18 | 19 | require('./service')(function (errSvc) { 20 | errSvc.inbound('*', tcp.server({port: 3001, host: '127.0.0.1'})) 21 | }) 22 | 23 | -------------------------------------------------------------------------------- /test/system/errorService/service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var mu = require('../../../packages/mu')() 18 | 19 | module.exports = function (cb) { 20 | mu.define({role: 'error', cmd: 'error'}, function (args, cb) { 21 | cb(mu.error('oh fek'), null) 22 | }) 23 | 24 | mu.define({role: 'error', cmd: 'crash'}, function (args, cb) { 25 | process.exit(1) 26 | }) 27 | 28 | cb(mu) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /test/system/multiResponseService/service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var mu = require('../../../packages/mu')() 18 | 19 | module.exports = function (cb) { 20 | mu.define({role: 'multi', cmd: 'one'}, function (args, cb) { 21 | cb(null, {data: 'response 1'}) 22 | cb(null, {data: 'response 2'}) 23 | }) 24 | 25 | cb(mu) 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/system/service1/service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | var createMu = require('../../../packages/mu') 17 | 18 | module.exports = function (cb) { 19 | var mu = createMu() 20 | 21 | mu.define({role: 's1', cmd: 'one'}, function (args, cb) { 22 | cb() 23 | }) 24 | 25 | mu.define({role: 's1', cmd: 'two'}, function (args, cb) { 26 | cb(null, {my: 'response'}) 27 | }) 28 | 29 | cb(mu) 30 | } 31 | 32 | -------------------------------------------------------------------------------- /test/system/service2/service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var mu = require('../../../packages/mu')() 18 | 19 | module.exports = function (cb) { 20 | mu.define({role: 's2', cmd: 'one'}, function (args, cb) { 21 | cb() 22 | }) 23 | 24 | mu.define({role: 's2', cmd: 'two'}, function (args, cb) { 25 | cb(null, {my: 'response'}) 26 | }) 27 | 28 | cb(mu) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /test/system/service3/service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var mu = require('../../../packages/mu')() 18 | 19 | module.exports = function (cb) { 20 | mu.define({role: 's3', cmd: 'one'}, function (args, cb) { 21 | mu.dispatch({role: 's1', cmd: 'two'}, function (err, result) { 22 | cb(err, result) 23 | }) 24 | }) 25 | 26 | cb(mu) 27 | } 28 | 29 | -------------------------------------------------------------------------------- /test/system/zeroResponseService/service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 3 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 4 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 6 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 8 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 10 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 11 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12 | * POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var mu = require('../../../packages/mu')() 18 | 19 | module.exports = function (cb) { 20 | mu.define({role: 'zero', cmd: 'one'}, function (args, cb) { 21 | }) 22 | 23 | cb(mu) 24 | } 25 | 26 | --------------------------------------------------------------------------------