├── .gitignore ├── LICENSE ├── README.md ├── lerna.json ├── package.json └── packages ├── serverless-plugin-transceiver ├── README.md ├── index.js ├── lib │ ├── docs.js │ └── sdk.js ├── package.json └── yarn.lock ├── serverless-transceiver-aws ├── README.md ├── decorator.js ├── index.js ├── package.json └── yarn.lock ├── serverless-transceiver-client ├── README.md ├── index.js └── package.json └── serverless-transceiver-service ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | .vscode 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 A Cloud Guru 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serverless Transceiver 2 | A plugin to support better inter-service communication. 3 | 4 | ## Why? 5 | In a microservices architecture, different services need to communicate with eachother. These messages might be async events or remote procedure calls. The messages and responses used during this communication need a stable format to allow the sender and receiver to process them. 6 | 7 | The goal of this plugin is to help formalise the contracts for inter-service communication in Serverless projects. Defining contracts for function events will have the following benefits: 8 | 9 | - Event validation (for sender and receiver) 10 | - Standard error handling 11 | - Documentation generation for internal services 12 | - SDK generation for internal services 13 | 14 | We also want to investigate whether we can tighten function invocation permissions by introduction a `internal` or `private` concept to functions. This will determine whether a function from another service is allowed to invoke this function. 15 | 16 | ## Contracts 17 | Contracts formalise the format of messages sent between services. There are two types of contracts of interest, the message contact and data contract. 18 | 19 | ### Message Contracts 20 | A message contract defines the structure of function events and results. 21 | 22 | Message contracts will be managed by an SDK. The contracts will be versioned so the format can be changed at a later date. 23 | 24 | I am proposing the following structures for message contracts. 25 | 26 | #### Event 27 | ```json 28 | { 29 | "version": "1", 30 | "data": { 31 | ... 32 | } 33 | } 34 | ``` 35 | 36 | #### Success Result 37 | ```json 38 | { 39 | "version": "1", 40 | "result": { 41 | "type": "success", 42 | "data": { 43 | ... 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | #### Validation Result 50 | ```json 51 | { 52 | "version": "1", 53 | "result": { 54 | "type": "validation", 55 | "validation": { 56 | "messages": [{ 57 | "message": "General validation" 58 | }, { 59 | "key": "name", 60 | "message": "Name is required" 61 | }] 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | ### Data Contracts 68 | A data contracts defines format of the event and result of a function. This will take the form of a JSON schema. 69 | 70 | ## SDKs 71 | 72 | ### Client 73 | - Invoke remote services 74 | - Validate events before invocation 75 | - Handle errors in a standard way 76 | 77 | 78 | ```js 79 | const transciever = require('serverless-transceiver-client') 80 | 81 | transciever 82 | .invoke('my-function', { message: 'hello' }) 83 | .then((result) => { 84 | 85 | }) 86 | .catch((err) => { 87 | if (err.type === 'validation') { 88 | // handle nicely 89 | } 90 | }) 91 | 92 | ``` 93 | 94 | ### Service 95 | - Validate received events 96 | - Method for returning standard responses 97 | 98 | 99 | ```js 100 | const transciever = require('serverless-transceiver-service') 101 | 102 | module.exports = (event, context, cb) => { 103 | const validation = validate(event) 104 | // success 105 | if (!validation.isValid) { 106 | cb(null, transciever.validation(validation.messages)) 107 | } 108 | 109 | // do some logic 110 | const result = mylogic(event.message) 111 | 112 | cb(null, transciever.success(result)) 113 | } 114 | ``` 115 | ## Serverless Plugin 116 | 117 | ### Define Data contracts 118 | Users should be able to define data contracts for functions the `serverless.yml` of a service. 119 | 120 | ```yaml 121 | service: my-service 122 | functions: 123 | my_function: 124 | handler: src/something.handler 125 | contract: 126 | schema: 127 | event: # JSON schema of function input 128 | type: object 129 | title: UserUpdateEvent 130 | properties: 131 | name: 132 | type: string 133 | age: 134 | type: integer 135 | minimum: 0 136 | required: 137 | - name 138 | result: # JSON schema of success result 139 | type: object 140 | title: UserUpdateResult 141 | properties: 142 | name: 143 | type: string 144 | age: 145 | type: integer 146 | minimum: 0 147 | required: 148 | - name 149 | 150 | my_other_function: 151 | handle: src/other.handler 152 | contract: ${file(./contracts/my_other_function.yml)} 153 | ``` 154 | 155 | ### Documentation Generation 156 | Automatically generate docs of callable functions and their contract. 157 | 158 | ### SDK Generation 159 | Automatically create an SDK for clients to use with this service 160 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.0.0-beta.38", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "0.0.1" 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "lerna": "2.0.0-beta.38" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/serverless-plugin-transceiver/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/serverless-transceiver/1042ff58b2dcb45921dd2fa121371a69cff60e2e/packages/serverless-plugin-transceiver/README.md -------------------------------------------------------------------------------- /packages/serverless-plugin-transceiver/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const BbPromise = require('bluebird') 4 | const path = require('path') 5 | 6 | class TransceiverPlugin { 7 | constructor(serverless, options) { 8 | this.serverless = serverless 9 | this.options = options 10 | 11 | Object.assign( 12 | this, 13 | run 14 | ) 15 | 16 | this.commands = { 17 | transceiver: { 18 | usage: 'Simulate λ locally', 19 | commands: { 20 | docs: { 21 | usage: 'Generate service documentation', 22 | lifecycleEvents: [ 23 | 'generate', 24 | ], 25 | options: { 26 | folder: { 27 | usage: 'Specify a output folder', 28 | shortcut: 'f', 29 | }, 30 | }, 31 | }, 32 | sdk: { 33 | usage: 'Generate service sdk', 34 | lifecycleEvents: [ 35 | 'generate', 36 | ], 37 | options: { 38 | folder: { 39 | usage: 'Specify a output folder', 40 | shortcut: 'f', 41 | }, 42 | }, 43 | }, 44 | }, 45 | }, 46 | } 47 | 48 | this.hooks = { 49 | 'transceiver:docs:generate': () => BbPromise.bind(this) 50 | .then(this.generateDocs) 51 | .then(out => this.serverless.cli.consoleLog(out)), 52 | 53 | 'transceiver:sdk:generate': () => BbPromise.bind(this) 54 | .then(this.generateSDK) 55 | .then(out => this.serverless.cli.consoleLog(out)), 56 | } 57 | } 58 | 59 | generateDocs() { 60 | } 61 | 62 | generateSDK() { 63 | } 64 | } 65 | 66 | module.exports = TransceiverPlugin 67 | -------------------------------------------------------------------------------- /packages/serverless-plugin-transceiver/lib/docs.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/serverless-transceiver/1042ff58b2dcb45921dd2fa121371a69cff60e2e/packages/serverless-plugin-transceiver/lib/docs.js -------------------------------------------------------------------------------- /packages/serverless-plugin-transceiver/lib/sdk.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/serverless-transceiver/1042ff58b2dcb45921dd2fa121371a69cff60e2e/packages/serverless-plugin-transceiver/lib/sdk.js -------------------------------------------------------------------------------- /packages/serverless-plugin-transceiver/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-plugin-transceiver", 3 | "version": "0.0.1", 4 | "description": "Serverless framework plugin for serverless transciever", 5 | "main": "index.js", 6 | "repository": "https://github.com/ACloudGuru/serverless-transceiver", 7 | "author": "John McKim ", 8 | "license": "MIT", 9 | "dependencies": { 10 | "bluebird": "^3.5.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/serverless-plugin-transceiver/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | bluebird@^3.5.0: 6 | version "3.5.0" 7 | resolved bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c 8 | -------------------------------------------------------------------------------- /packages/serverless-transceiver-aws/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/serverless-transceiver/1042ff58b2dcb45921dd2fa121371a69cff60e2e/packages/serverless-transceiver-aws/README.md -------------------------------------------------------------------------------- /packages/serverless-transceiver-aws/decorator.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const node43 = (handler, schema) => { 4 | return (event, context, cb) => { 5 | // validate event against schema 6 | 7 | handler(event, context, cb) 8 | } 9 | } 10 | 11 | module.exports = { 12 | node43, 13 | } -------------------------------------------------------------------------------- /packages/serverless-transceiver-aws/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const AWS = require('aws-sdk') 4 | 5 | // Could add concept of async invocations here through options i.e. InvocationType: Event 6 | const invoke = (lambda, lambdaName, data) => { 7 | const params = { 8 | FunctionName: lambdaName, 9 | InvocationType: 'RequestResponse', 10 | Payload: JSON.stringify(event), 11 | }; 12 | 13 | return lambda.invoke(params).promise().then((result) => { 14 | console.log('Received result: ', result); 15 | 16 | if(result.FunctionError) { 17 | return Promise.reject(new Error(`Error on ${lambdaName}`)); 18 | } 19 | 20 | const data = JSON.parse(result.Payload); 21 | return data 22 | }); 23 | } 24 | 25 | 26 | module.exports = (options) => { 27 | const lambda = new AWS.Lambda(options) 28 | 29 | return { 30 | invoke: (lambdaName, data) => invoke(lambda, lambdaName, data), 31 | } 32 | } -------------------------------------------------------------------------------- /packages/serverless-transceiver-aws/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-transceiver-aws", 3 | "version": "0.0.1", 4 | "description": "AWS adapter for Serverless Transceiver", 5 | "main": "index.js", 6 | "repository": "https://github.com/ACloudGuru/serverless-transceiver", 7 | "author": "John McKim ", 8 | "license": "MIT", 9 | "dependencies": { 10 | "aws-sdk": "^2.28.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/serverless-transceiver-aws/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | aws-sdk@^2.28.0: 6 | version "2.28.0" 7 | resolved aws-sdk-2.28.0.tgz#0b7628c5d48820187332c5a30835945c41f84f46 8 | dependencies: 9 | buffer "4.9.1" 10 | crypto-browserify "1.0.9" 11 | jmespath "0.15.0" 12 | querystring "0.2.0" 13 | sax "1.1.5" 14 | url "0.10.3" 15 | uuid "3.0.0" 16 | xml2js "0.4.15" 17 | xmlbuilder "2.6.2" 18 | 19 | base64-js@^1.0.2: 20 | version "1.2.0" 21 | resolved base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1 22 | 23 | buffer@4.9.1: 24 | version "4.9.1" 25 | resolved buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298 26 | dependencies: 27 | base64-js "^1.0.2" 28 | ieee754 "^1.1.4" 29 | isarray "^1.0.0" 30 | 31 | crypto-browserify@1.0.9: 32 | version "1.0.9" 33 | resolved crypto-browserify-1.0.9.tgz#cc5449685dfb85eb11c9828acc7cb87ab5bbfcc0 34 | 35 | ieee754@^1.1.4: 36 | version "1.1.8" 37 | resolved ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4 38 | 39 | isarray@^1.0.0: 40 | version "1.0.0" 41 | resolved isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11 42 | 43 | jmespath@0.15.0: 44 | version "0.15.0" 45 | resolved jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217 46 | 47 | lodash@~3.5.0: 48 | version "3.5.0" 49 | resolved lodash-3.5.0.tgz#19bb3f4d51278f0b8c818ed145c74ecf9fe40e6d 50 | 51 | punycode@1.3.2: 52 | version "1.3.2" 53 | resolved punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d 54 | 55 | querystring@0.2.0: 56 | version "0.2.0" 57 | resolved querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620 58 | 59 | sax@1.1.5, sax@>=0.6.0: 60 | version "1.1.5" 61 | resolved sax-1.1.5.tgz#1da50a8d00cdecd59405659f5ff85349fe773743 62 | 63 | url@0.10.3: 64 | version "0.10.3" 65 | resolved url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64 66 | dependencies: 67 | punycode "1.3.2" 68 | querystring "0.2.0" 69 | 70 | uuid@3.0.0: 71 | version "3.0.0" 72 | resolved uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728 73 | 74 | xml2js@0.4.15: 75 | version "0.4.15" 76 | resolved xml2js-0.4.15.tgz#95cd03ff2dd144ec28bc6273bf2b2890c581ad0c 77 | dependencies: 78 | sax ">=0.6.0" 79 | xmlbuilder ">=2.4.6" 80 | 81 | xmlbuilder@2.6.2, xmlbuilder@>=2.4.6: 82 | version "2.6.2" 83 | resolved xmlbuilder-2.6.2.tgz#f916f6d10d45dc171b1be2e6e673fb6e0cc35d0a 84 | dependencies: 85 | lodash "~3.5.0" 86 | -------------------------------------------------------------------------------- /packages/serverless-transceiver-client/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/serverless-transceiver/1042ff58b2dcb45921dd2fa121371a69cff60e2e/packages/serverless-transceiver-client/README.md -------------------------------------------------------------------------------- /packages/serverless-transceiver-client/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const MESSAGE_VERSION = '1' 4 | 5 | const invoke = (provider, func, data) => { 6 | const message = { 7 | version: MESSAGE_VERSION, 8 | data: data 9 | } 10 | 11 | return provider 12 | .invoke(func, data) 13 | .then((result) => { 14 | // check result type and handle appropriately 15 | if (result.type === 'validation') { 16 | return Promise.reject() 17 | } 18 | 19 | }).catch((err) => { 20 | 21 | }) 22 | } 23 | 24 | module.exports = (provider) => { 25 | return { 26 | invoke: (func, data) => invoke(provider, func, data) 27 | } 28 | } -------------------------------------------------------------------------------- /packages/serverless-transceiver-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-transceiver-client", 3 | "version": "0.0.1", 4 | "description": "Client SDK for serverless transciever", 5 | "main": "index.js", 6 | "repository": "https://github.com/ACloudGuru/serverless-transceiver", 7 | "author": "John McKim ", 8 | "license": "MIT" 9 | } 10 | -------------------------------------------------------------------------------- /packages/serverless-transceiver-service/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/serverless-transceiver/1042ff58b2dcb45921dd2fa121371a69cff60e2e/packages/serverless-transceiver-service/README.md -------------------------------------------------------------------------------- /packages/serverless-transceiver-service/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const MESSAGE_VERSION = '1' 4 | 5 | const createResult = (type, key, data) => ({ 6 | version: MESSAGE_VERSION, 7 | result: { 8 | type, 9 | [key]: data, 10 | }, 11 | }) 12 | 13 | const success = (data) => createResult('success', 'data', data) 14 | const validation = (messages) => createResult('validation', 'validation', { messages }) 15 | 16 | module.exports = { 17 | success, 18 | validation, 19 | } -------------------------------------------------------------------------------- /packages/serverless-transceiver-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-transceiver-service", 3 | "version": "0.0.1", 4 | "description": "Service SDK for serverless transciever", 5 | "main": "index.js", 6 | "repository": "https://github.com/ACloudGuru/serverless-transceiver", 7 | "author": "John McKim ", 8 | "license": "MIT" 9 | } 10 | --------------------------------------------------------------------------------