mirror of https://github.com/Seich/Beau.git
				
				
				
			Merge pull request #7 from Seich/next-plugins
Refactored Beau's Plugin System
This commit is contained in:
		
						commit
						490bc85174
					
				
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -25,7 +25,7 @@ | |||
|   }, | ||||
|   "repository": "git@github.com:Seich/Beau.git", | ||||
|   "devDependencies": { | ||||
|     "jest": "22.0.4" | ||||
|     "jest": "^22.4.0" | ||||
|   }, | ||||
|   "jest": { | ||||
|     "testEnvironment": "node", | ||||
|  |  | |||
|  | @ -0,0 +1,11 @@ | |||
| class DynamicValues { | ||||
| 	constructor(registry, settings = {}) { | ||||
| 		registry.defineDynamicValue('add', this.add); | ||||
| 	} | ||||
| 
 | ||||
| 	add(x, y) { | ||||
| 		return x + y; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = DynamicValues; | ||||
|  | @ -0,0 +1,19 @@ | |||
| class Modifiers { | ||||
| 	constructor(registry, settings = {}) { | ||||
| 		registry.addPreRequestModifier(this.preRequest); | ||||
| 		registry.addPostRequestModifier(this.postRequest); | ||||
| 	} | ||||
| 
 | ||||
| 	preRequest(request, orig) { | ||||
| 		request.headers.preRequestModifier = true; | ||||
| 		return request; | ||||
| 	} | ||||
| 
 | ||||
| 	postRequest(response, orig) { | ||||
| 		response.body = 'Hello World'; | ||||
| 		response.response.body = 'Hello World'; | ||||
| 		return response; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = Modifiers; | ||||
|  | @ -1,19 +1,3 @@ | |||
| module.exports = function(name) { | ||||
| 	return function(settings) { | ||||
| 		let response = { | ||||
| 			name: `${name}`, | ||||
| 			preRequest(request) { | ||||
| 				request.wasModified = true; | ||||
| 
 | ||||
| 				return request; | ||||
| 			}, | ||||
| 
 | ||||
| 			postResponse(response) { | ||||
| 				response.changed = true; | ||||
| 				return response; | ||||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		return response; | ||||
| 	}; | ||||
| 	return require(name); | ||||
| }; | ||||
|  |  | |||
|  | @ -12,7 +12,33 @@ Beau { | |||
|     "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|     "ENVIRONMENT": Object {}, | ||||
|     "HOSTS": Array [], | ||||
|     "PLUGINS": Array [], | ||||
|     "PLUGINS": Plugins { | ||||
|       "context": Object {}, | ||||
|       "registry": Object { | ||||
|         "dynamicValues": Array [], | ||||
|         "postRequestModifiers": Array [], | ||||
|         "preRequestModifiers": Array [], | ||||
|       }, | ||||
|     }, | ||||
|     "REQUESTS": Array [ | ||||
|       Object { | ||||
|         "ALIAS": "get-post", | ||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|         "HEADERS": Object { | ||||
|           "authentication": "hello", | ||||
|         }, | ||||
|         "REQUEST": "GET /posts/1", | ||||
|       }, | ||||
|       Object { | ||||
|         "ALIAS": "user", | ||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|         "HEADERS": Object { | ||||
|           "authentication": "hello", | ||||
|           "hello": "world", | ||||
|         }, | ||||
|         "REQUEST": "GET /user", | ||||
|       }, | ||||
|     ], | ||||
|     "VERSION": 1, | ||||
|     "configKeys": Array [ | ||||
|       "VERSION", | ||||
|  | @ -48,7 +74,9 @@ Beau { | |||
|       "endpoint": "http://jsonplaceholder.typicode.com", | ||||
|       "version": 1, | ||||
|     }, | ||||
|     "requests": Array [ | ||||
|   }, | ||||
|   "requests": RequestList { | ||||
|     "REQUESTS": Array [ | ||||
|       Object { | ||||
|         "ALIAS": "get-post", | ||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|  | @ -67,8 +95,6 @@ Beau { | |||
|         "REQUEST": "GET /user", | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   "requests": RequestList { | ||||
|     "cache": RequestCache { | ||||
|       "$cache": Object { | ||||
|         "$env": Object {}, | ||||
|  | @ -84,7 +110,33 @@ Beau { | |||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|       "ENVIRONMENT": Object {}, | ||||
|       "HOSTS": Array [], | ||||
|       "PLUGINS": Array [], | ||||
|       "PLUGINS": Plugins { | ||||
|         "context": Object {}, | ||||
|         "registry": Object { | ||||
|           "dynamicValues": Array [], | ||||
|           "postRequestModifiers": Array [], | ||||
|           "preRequestModifiers": Array [], | ||||
|         }, | ||||
|       }, | ||||
|       "REQUESTS": Array [ | ||||
|         Object { | ||||
|           "ALIAS": "get-post", | ||||
|           "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|           "HEADERS": Object { | ||||
|             "authentication": "hello", | ||||
|           }, | ||||
|           "REQUEST": "GET /posts/1", | ||||
|         }, | ||||
|         Object { | ||||
|           "ALIAS": "user", | ||||
|           "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|           "HEADERS": Object { | ||||
|             "authentication": "hello", | ||||
|             "hello": "world", | ||||
|           }, | ||||
|           "REQUEST": "GET /user", | ||||
|         }, | ||||
|       ], | ||||
|       "VERSION": 1, | ||||
|       "configKeys": Array [ | ||||
|         "VERSION", | ||||
|  | @ -120,25 +172,6 @@ Beau { | |||
|         "endpoint": "http://jsonplaceholder.typicode.com", | ||||
|         "version": 1, | ||||
|       }, | ||||
|       "requests": Array [ | ||||
|         Object { | ||||
|           "ALIAS": "get-post", | ||||
|           "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|           "HEADERS": Object { | ||||
|             "authentication": "hello", | ||||
|           }, | ||||
|           "REQUEST": "GET /posts/1", | ||||
|         }, | ||||
|         Object { | ||||
|           "ALIAS": "user", | ||||
|           "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|           "HEADERS": Object { | ||||
|             "authentication": "hello", | ||||
|             "hello": "world", | ||||
|           }, | ||||
|           "REQUEST": "GET /user", | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     "list": Array [ | ||||
|       Request { | ||||
|  | @ -159,6 +192,14 @@ Beau { | |||
|           }, | ||||
|           "REQUEST": "GET /posts/1", | ||||
|         }, | ||||
|         "plugins": Plugins { | ||||
|           "context": Object {}, | ||||
|           "registry": Object { | ||||
|             "dynamicValues": Array [], | ||||
|             "postRequestModifiers": Array [], | ||||
|             "preRequestModifiers": Array [], | ||||
|           }, | ||||
|         }, | ||||
|       }, | ||||
|       Request { | ||||
|         "ALIAS": "user", | ||||
|  | @ -180,26 +221,14 @@ Beau { | |||
|           }, | ||||
|           "REQUEST": "GET /user", | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|     "modifiers": Array [], | ||||
|     "requests": Array [ | ||||
|       Object { | ||||
|         "ALIAS": "get-post", | ||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|         "HEADERS": Object { | ||||
|           "authentication": "hello", | ||||
|         "plugins": Plugins { | ||||
|           "context": Object {}, | ||||
|           "registry": Object { | ||||
|             "dynamicValues": Array [], | ||||
|             "postRequestModifiers": Array [], | ||||
|             "preRequestModifiers": Array [], | ||||
|           }, | ||||
|         }, | ||||
|         "REQUEST": "GET /posts/1", | ||||
|       }, | ||||
|       Object { | ||||
|         "ALIAS": "user", | ||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|         "HEADERS": Object { | ||||
|           "authentication": "hello", | ||||
|           "hello": "world", | ||||
|         }, | ||||
|         "REQUEST": "GET /user", | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|  |  | |||
|  | @ -41,7 +41,68 @@ Config { | |||
|       "host": "info", | ||||
|     }, | ||||
|   ], | ||||
|   "PLUGINS": Array [], | ||||
|   "PLUGINS": Plugins { | ||||
|     "context": Object {}, | ||||
|     "registry": Object { | ||||
|       "dynamicValues": Array [], | ||||
|       "postRequestModifiers": Array [], | ||||
|       "preRequestModifiers": Array [], | ||||
|     }, | ||||
|   }, | ||||
|   "REQUESTS": Array [ | ||||
|     Object { | ||||
|       "ALIAS": "e1", | ||||
|       "ENDPOINT": "http://example.org", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "mars", | ||||
|       }, | ||||
|       "REQUEST": "GET /e1", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "com:e2", | ||||
|       "ENDPOINT": "http://example.com", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "hello", | ||||
|       }, | ||||
|       "REQUEST": "GET /e2", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "com:posts", | ||||
|       "ENDPOINT": "http://example.com", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "hello", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "net:e3", | ||||
|       "ENDPOINT": "http://example.net", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "bye", | ||||
|       }, | ||||
|       "REQUEST": "GET /e3", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "net:posts", | ||||
|       "ENDPOINT": "http://example.net", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "bye", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "info:posts", | ||||
|       "ENDPOINT": "http://example.info", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "mars", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts", | ||||
|     }, | ||||
|   ], | ||||
|   "VERSION": 1, | ||||
|   "configKeys": Array [ | ||||
|     "VERSION", | ||||
|  | @ -101,60 +162,6 @@ Config { | |||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   "requests": Array [ | ||||
|     Object { | ||||
|       "ALIAS": "e1", | ||||
|       "ENDPOINT": "http://example.org", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "mars", | ||||
|       }, | ||||
|       "REQUEST": "GET /e1", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "com:e2", | ||||
|       "ENDPOINT": "http://example.com", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "hello", | ||||
|       }, | ||||
|       "REQUEST": "GET /e2", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "com:posts", | ||||
|       "ENDPOINT": "http://example.com", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "hello", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "net:e3", | ||||
|       "ENDPOINT": "http://example.net", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "bye", | ||||
|       }, | ||||
|       "REQUEST": "GET /e3", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "net:posts", | ||||
|       "ENDPOINT": "http://example.net", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "world", | ||||
|         "world": "bye", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "info:posts", | ||||
|       "ENDPOINT": "http://example.info", | ||||
|       "HEADERS": Object { | ||||
|         "hello": "mars", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts", | ||||
|     }, | ||||
|   ], | ||||
| } | ||||
| `; | ||||
| 
 | ||||
|  | @ -169,7 +176,33 @@ Config { | |||
|   "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|   "ENVIRONMENT": Object {}, | ||||
|   "HOSTS": Array [], | ||||
|   "PLUGINS": Array [], | ||||
|   "PLUGINS": Plugins { | ||||
|     "context": Object {}, | ||||
|     "registry": Object { | ||||
|       "dynamicValues": Array [], | ||||
|       "postRequestModifiers": Array [], | ||||
|       "preRequestModifiers": Array [], | ||||
|     }, | ||||
|   }, | ||||
|   "REQUESTS": Array [ | ||||
|     Object { | ||||
|       "ALIAS": "get-post", | ||||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|       "HEADERS": Object { | ||||
|         "authentication": "hello", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts/1", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "user", | ||||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|       "HEADERS": Object { | ||||
|         "authentication": "hello", | ||||
|         "hello": "world", | ||||
|       }, | ||||
|       "REQUEST": "GET /user", | ||||
|     }, | ||||
|   ], | ||||
|   "VERSION": 1, | ||||
|   "configKeys": Array [ | ||||
|     "VERSION", | ||||
|  | @ -205,24 +238,5 @@ Config { | |||
|     "endpoint": "http://jsonplaceholder.typicode.com", | ||||
|     "version": 1, | ||||
|   }, | ||||
|   "requests": Array [ | ||||
|     Object { | ||||
|       "ALIAS": "get-post", | ||||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|       "HEADERS": Object { | ||||
|         "authentication": "hello", | ||||
|       }, | ||||
|       "REQUEST": "GET /posts/1", | ||||
|     }, | ||||
|     Object { | ||||
|       "ALIAS": "user", | ||||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", | ||||
|       "HEADERS": Object { | ||||
|         "authentication": "hello", | ||||
|         "hello": "world", | ||||
|       }, | ||||
|       "REQUEST": "GET /user", | ||||
|     }, | ||||
|   ], | ||||
| } | ||||
| `; | ||||
|  |  | |||
|  | @ -0,0 +1,49 @@ | |||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||
| 
 | ||||
| exports[`Beau's plugin system Dynamic Values should look for dynamic values executing and replacing them 1`] = ` | ||||
| Object { | ||||
|   "body": "Hello World", | ||||
|   "request": Object { | ||||
|     "body": undefined, | ||||
|     "endpoint": "http://example.com/hello/3", | ||||
|     "headers": Object { | ||||
|       "count": "3", | ||||
|       "preRequestModifier": true, | ||||
|     }, | ||||
|   }, | ||||
|   "response": Object { | ||||
|     "body": "Hello World", | ||||
|     "headers": Array [], | ||||
|     "status": 200, | ||||
|   }, | ||||
| } | ||||
| `; | ||||
| 
 | ||||
| exports[`Beau's plugin system Request Modifiers should modify the request and response using modifiers. 1`] = ` | ||||
| Object { | ||||
|   "body": "Hello World", | ||||
|   "request": Object { | ||||
|     "body": undefined, | ||||
|     "endpoint": "http://example.com/user", | ||||
|     "headers": Object { | ||||
|       "preRequestModifier": true, | ||||
|     }, | ||||
|   }, | ||||
|   "response": Object { | ||||
|     "body": "Hello World", | ||||
|     "headers": Array [], | ||||
|     "status": 200, | ||||
|   }, | ||||
| } | ||||
| `; | ||||
| 
 | ||||
| exports[`Beau's plugin system shouldn't do anything when given an empty array. 1`] = ` | ||||
| Plugins { | ||||
|   "context": Object {}, | ||||
|   "registry": Object { | ||||
|     "dynamicValues": Array [], | ||||
|     "postRequestModifiers": Array [], | ||||
|     "preRequestModifiers": Array [], | ||||
|   }, | ||||
| } | ||||
| `; | ||||
|  | @ -35,22 +35,3 @@ Object { | |||
|   }, | ||||
| } | ||||
| `; | ||||
| 
 | ||||
| exports[`Request should use modifiers 1`] = ` | ||||
| Array [ | ||||
|   Array [ | ||||
|     Object { | ||||
|       "endpoint": "http://martianwabbit.com/user", | ||||
|       "headers": Object {}, | ||||
|       "method": "GET", | ||||
|       "payload": Object {}, | ||||
|       "query": Object {}, | ||||
|     }, | ||||
|     Object { | ||||
|       "alias": "show", | ||||
|       "endpoint": "http://martianwabbit.com", | ||||
|       "request": "GET /user", | ||||
|     }, | ||||
|   ], | ||||
| ] | ||||
| `; | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| exports[`RequestList should execute requests by alias. 1`] = ` | ||||
| Object { | ||||
|   "body": "{\\"hello\\": \\"world\\"}", | ||||
|   "changed": true, | ||||
|   "request": Object { | ||||
|     "body": Object { | ||||
|       "lastname": "Diaz", | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ describe('Config', () => { | |||
|         `);
 | ||||
| 
 | ||||
|     const config = new Config(doc); | ||||
|     expect(Object.keys(config.requests).length).toBe(4); | ||||
|     expect(Object.keys(config.REQUESTS).length).toBe(4); | ||||
|   }); | ||||
| 
 | ||||
|   it('should set up defaults for all requests', () => { | ||||
|  | @ -53,7 +53,7 @@ describe('Config', () => { | |||
|     const config = new Config(doc); | ||||
| 
 | ||||
|     expect(config).toMatchSnapshot(); | ||||
|     Object.values(config.requests).forEach(r => { | ||||
|     Object.values(config.REQUESTS).forEach(r => { | ||||
|       expect(r.HEADERS.authentication).toMatch('hello'); | ||||
|     }); | ||||
|   }); | ||||
|  | @ -115,8 +115,8 @@ describe('Config', () => { | |||
| 
 | ||||
|     let config = new Config(doc); | ||||
| 
 | ||||
|     expect(config.requests[0].ALIAS).toBe('test1:posts'); | ||||
|     expect(config.requests[1].ALIAS).toBe('test2:posts'); | ||||
|     expect(config.REQUESTS[0].ALIAS).toBe('test1:posts'); | ||||
|     expect(config.REQUESTS[1].ALIAS).toBe('test2:posts'); | ||||
|   }); | ||||
| 
 | ||||
|   it(`should throw if host doesn't have a host key`, () => { | ||||
|  | @ -153,6 +153,6 @@ describe('Config', () => { | |||
|         `);
 | ||||
| 
 | ||||
|     let config = new Config(doc); | ||||
|     expect(config.requests[0].HEADERS.hello).toBe(1); | ||||
|     expect(config.REQUESTS[0].HEADERS.hello).toBe(1); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -0,0 +1,94 @@ | |||
| const yaml = require('js-yaml'); | ||||
| const Config = require('../config'); | ||||
| const Plugins = require('../plugins'); | ||||
| const Request = require('../request'); | ||||
| const RequestCache = require('../requestCache'); | ||||
| 
 | ||||
| describe(`Beau's plugin system`, () => { | ||||
|     let config; | ||||
|     let request; | ||||
|     let plugins; | ||||
| 
 | ||||
|     beforeEach(() => { | ||||
|         const doc = yaml.safeLoad(` | ||||
|             version: 1 | ||||
|             endpoint: 'http://example.com' | ||||
| 
 | ||||
|             plugins: | ||||
|                 - Modifiers: | ||||
|                     data: hi | ||||
|                 - DynamicValues | ||||
| 
 | ||||
|             GET /posts/$[add(1, 1)]: get-post | ||||
|         `);
 | ||||
| 
 | ||||
|         config = new Config(doc); | ||||
|         plugins = config.PLUGINS; | ||||
|     }); | ||||
| 
 | ||||
|     it('should load all plugins', () => { | ||||
|         expect(plugins.registry.preRequestModifiers.length).toBe(1); | ||||
|         expect(plugins.registry.postRequestModifiers.length).toBe(1); | ||||
|         expect(plugins.registry.dynamicValues.length).toBe(1); | ||||
|     }); | ||||
| 
 | ||||
|     it(`should throw if given an invalid configuration`, () => { | ||||
|         expect(() => new Plugins([{ test1: true, test2: true }])).toThrow(); | ||||
|     }); | ||||
| 
 | ||||
|     it(`shouldn't do anything when given an empty array.`, () => { | ||||
|         expect(new Plugins()).toMatchSnapshot(); | ||||
|     }); | ||||
| 
 | ||||
|     describe(`Request Modifiers`, () => { | ||||
|         beforeEach(() => { | ||||
|             request = new Request( | ||||
|                 { | ||||
|                     request: 'POST /user', | ||||
|                     endpoint: 'http://example.com', | ||||
|                     alias: 'update' | ||||
|                 }, | ||||
|                 plugins | ||||
|             ); | ||||
|         }); | ||||
| 
 | ||||
|         it(`should modify the request and response using modifiers.`, async () => { | ||||
|             await expect(request.exec()).resolves.toMatchSnapshot(); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     describe(`Dynamic Values`, () => { | ||||
|         beforeEach(() => { | ||||
|             request = new Request( | ||||
|                 { | ||||
|                     request: 'PATCH /hello/$[add(1, 2)]', | ||||
|                     endpoint: 'http://example.com', | ||||
|                     alias: 'say-hello', | ||||
|                     headers: { | ||||
|                         count: '$[add(1, $value2)]' | ||||
|                     } | ||||
|                 }, | ||||
|                 plugins | ||||
|             ); | ||||
|         }); | ||||
| 
 | ||||
|         it(`should look for dynamic values executing and replacing them`, async () => { | ||||
|             let cache = new RequestCache(); | ||||
|             cache.add('$value2', '2'); | ||||
| 
 | ||||
|             let req = await request.exec(cache); | ||||
| 
 | ||||
|             expect(req).toHaveProperty('request.headers.count', '3'); | ||||
|             expect(req).toMatchSnapshot(); | ||||
|         }); | ||||
| 
 | ||||
|         it(`should throw when calling an undefined dynamic value`, async () => { | ||||
|             request = new Request({ | ||||
|                 request: 'POST /hello/$[notAvailable(1, 2)]', | ||||
|                 alias: 'say-hello' | ||||
|             }); | ||||
| 
 | ||||
|             await expect(request.exec()).rejects.toThrow(); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
|  | @ -64,7 +64,7 @@ describe('Request', () => { | |||
| 	}); | ||||
| 
 | ||||
| 	it('should execute a request', async () => { | ||||
| 		await expect(request.exec([], cache)).resolves.toMatchSnapshot(); | ||||
| 		await expect(request.exec(cache)).resolves.toMatchSnapshot(); | ||||
| 		await expect( | ||||
| 			requestWithoutDependencies.exec() | ||||
| 		).resolves.toMatchSnapshot(); | ||||
|  | @ -74,21 +74,4 @@ describe('Request', () => { | |||
| 		requestPromiseNativeMock.fail = true; | ||||
| 		await expect(requestWithoutDependencies.exec()).rejects.toThrow(Error); | ||||
| 	}); | ||||
| 
 | ||||
| 	it('should use modifiers', async () => { | ||||
| 		const preRequest = jest.fn(); | ||||
| 		const withPreRequest = [{ preRequest }]; | ||||
| 
 | ||||
| 		const notCalled = jest.fn(); | ||||
| 		const nonModifiers = [{ notCalled }]; | ||||
| 
 | ||||
| 		await requestWithoutDependencies.exec(withPreRequest); | ||||
| 
 | ||||
| 		expect(preRequest).toHaveBeenCalled(); | ||||
| 		expect(preRequest.mock.calls).toMatchSnapshot(); | ||||
| 
 | ||||
| 		await requestWithoutDependencies.exec(nonModifiers); | ||||
| 
 | ||||
| 		expect(notCalled).not.toHaveBeenCalled(); | ||||
| 	}); | ||||
| }); | ||||
|  |  | |||
|  | @ -12,17 +12,6 @@ describe('RequestList', () => { | |||
| 	const doc = { | ||||
| 		ENDPOINT: endpoint, | ||||
| 		ENVIRONMENT: env, | ||||
| 		PLUGINS: [ | ||||
| 			{ | ||||
| 				'beau-jwt': { | ||||
| 					data: { | ||||
| 						secret: 'shhh.', | ||||
| 						userId: 412 | ||||
| 					} | ||||
| 				} | ||||
| 			}, | ||||
| 			'beau-document' | ||||
| 		], | ||||
| 		'GET /post': { alias: 'get-posts' }, | ||||
| 		'POST /user': { | ||||
| 			alias: 'user', | ||||
|  | @ -38,7 +27,12 @@ describe('RequestList', () => { | |||
| 		requestPromiseNativeMock.fail = false; | ||||
| 
 | ||||
| 		let config = new Config(doc); | ||||
| 		requests = new RequestList(config.requests, config); | ||||
| 		requests = new RequestList(config); | ||||
| 	}); | ||||
| 
 | ||||
| 	it('should allow an empty request list', () => { | ||||
| 		requests = new RequestList(); | ||||
| 		expect(requests.list.length).toBe(0); | ||||
| 	}); | ||||
| 
 | ||||
| 	it('should load valid requests', () => { | ||||
|  | @ -49,12 +43,6 @@ describe('RequestList', () => { | |||
| 		requests.fetchDependencies(['get-posts']); | ||||
| 	}); | ||||
| 
 | ||||
| 	it('should load plugins', () => { | ||||
| 		const pluginLessList = new RequestList(); | ||||
| 		expect(requests.modifiers.length).toBe(2); | ||||
| 		expect(pluginLessList.modifiers.length).toBe(0); | ||||
| 	}); | ||||
| 
 | ||||
| 	it('should execute requests by alias.', async () => { | ||||
| 		await expect(requests.execByAlias('user')).resolves.toMatchSnapshot(); | ||||
| 	}); | ||||
|  | @ -83,6 +71,6 @@ describe('RequestList', () => { | |||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		expect(() => new RequestList(config.requests, config)).toThrow(); | ||||
| 		expect(() => new RequestList(config, config)).toThrow(); | ||||
| 	}); | ||||
| }); | ||||
|  |  | |||
|  | @ -4,8 +4,7 @@ const Config = require('./config'); | |||
| class Beau { | ||||
| 	constructor(doc, env = {}) { | ||||
| 		this.config = new Config(doc, env); | ||||
| 
 | ||||
| 		this.requests = new RequestList(this.config.requests, this.config); | ||||
| 		this.requests = new RequestList(this.config); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| const deepMerge = require('deepmerge'); | ||||
| const { requestRegex, UpperCaseKeys } = require('./shared'); | ||||
| const Plugins = require('./plugins'); | ||||
| 
 | ||||
| class Config { | ||||
| 	constructor(doc, env = {}) { | ||||
|  | @ -23,7 +24,7 @@ class Config { | |||
| 
 | ||||
| 		this.ENVIRONMENT = deepMerge(this.ENVIRONMENT, env); | ||||
| 
 | ||||
| 		this.requests = []; | ||||
| 		this.REQUESTS = []; | ||||
| 
 | ||||
| 		this.loadRequests(doc, { | ||||
| 			DEFAULTS: this.DEFAULTS, | ||||
|  | @ -31,6 +32,8 @@ class Config { | |||
| 		}); | ||||
| 
 | ||||
| 		this.loadHosts(this.HOSTS, config); | ||||
| 
 | ||||
| 		this.PLUGINS = new Plugins(this.PLUGINS); | ||||
| 	} | ||||
| 
 | ||||
| 	loadHosts(hosts, rootConfig) { | ||||
|  | @ -77,7 +80,7 @@ class Config { | |||
| 				return deepMerge(defaults, request); | ||||
| 			}); | ||||
| 
 | ||||
| 		this.requests = this.requests.concat(requests); | ||||
| 		this.REQUESTS = this.REQUESTS.concat(requests); | ||||
| 	} | ||||
| 
 | ||||
| 	loadConfig(host) { | ||||
|  |  | |||
|  | @ -0,0 +1,92 @@ | |||
| const vm = require('vm'); | ||||
| const requireg = require('requireg'); | ||||
| const deepmerge = require('deepmerge'); | ||||
| const { toKebabCase, dynamicValueRegex, replaceInObject } = require('./shared'); | ||||
| 
 | ||||
| class Plugins { | ||||
| 	constructor(plugins = []) { | ||||
| 		this.registry = { | ||||
| 			preRequestModifiers: [], | ||||
| 			postRequestModifiers: [], | ||||
| 			dynamicValues: [] | ||||
| 		}; | ||||
| 
 | ||||
| 		this.context = {}; | ||||
| 
 | ||||
| 		plugins.forEach(plugin => this.loadPlugin(plugin)); | ||||
| 	} | ||||
| 
 | ||||
| 	loadPlugin(plugin) { | ||||
| 		let name = plugin; | ||||
| 		let settings = {}; | ||||
| 
 | ||||
| 		if (typeof plugin === 'object') { | ||||
| 			let keys = Object.keys(plugin); | ||||
| 
 | ||||
| 			if (keys.length !== 1) { | ||||
| 				throw new Error(`Plugin items should contain only one key.`); | ||||
| 			} | ||||
| 
 | ||||
| 			name = Object.keys(plugin)[0]; | ||||
| 			settings = plugin[name]; | ||||
| 		} | ||||
| 
 | ||||
| 		plugin = requireg(`./beau-${toKebabCase(name)}`); | ||||
| 		new plugin(this, settings); | ||||
| 	} | ||||
| 
 | ||||
| 	executeModifier(modifier, obj, orig) { | ||||
| 		let result = deepmerge({}, obj); | ||||
| 
 | ||||
| 		this.registry[modifier].forEach( | ||||
| 			modifier => (result = modifier(result, orig)) | ||||
| 		); | ||||
| 
 | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	execPreRequestModifiers(request, originalRequest) { | ||||
| 		return this.executeModifier( | ||||
| 			'preRequestModifiers', | ||||
| 			request, | ||||
| 			originalRequest | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	execPostRequestModifiers(response, originalRequest) { | ||||
| 		return this.executeModifier( | ||||
| 			'postRequestModifiers', | ||||
| 			response, | ||||
| 			originalRequest | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	replaceDynamicValues(obj) { | ||||
| 		return replaceInObject(obj, val => { | ||||
| 			try { | ||||
| 				return val.replace(dynamicValueRegex, (match, call) => { | ||||
| 					return vm.runInContext(call, this.context); | ||||
| 				}); | ||||
| 			} catch (e) { | ||||
| 				throw new Error(`DynamicValue: ` + e); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	addPreRequestModifier(modifier) { | ||||
| 		this.registry.preRequestModifiers.push(modifier); | ||||
| 	} | ||||
| 
 | ||||
| 	addPostRequestModifier(modifier) { | ||||
| 		this.registry.postRequestModifiers.push(modifier); | ||||
| 	} | ||||
| 
 | ||||
| 	defineDynamicValue(name, fn) { | ||||
| 		this.registry.dynamicValues.push({ name, fn }); | ||||
| 		this.context[name] = fn; | ||||
| 
 | ||||
| 		vm.createContext(this.context); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = Plugins; | ||||
|  | @ -1,4 +1,8 @@ | |||
| const request = require('request-promise-native'); | ||||
| const RequestList = require('./requestList'); | ||||
| const RequestCache = require('./requestCache'); | ||||
| const Plugins = require('./plugins'); | ||||
| 
 | ||||
| const { | ||||
| 	httpVerbs, | ||||
| 	requestRegex, | ||||
|  | @ -6,12 +10,11 @@ const { | |||
| 	UpperCaseKeys, | ||||
| 	removeOptionalKeys | ||||
| } = require('./shared'); | ||||
| const RequestList = require('./requestList'); | ||||
| const RequestCache = require('./requestCache'); | ||||
| 
 | ||||
| class Request { | ||||
| 	constructor(req) { | ||||
| 	constructor(req, plugins = new Plugins()) { | ||||
| 		this.originalRequest = req; | ||||
| 		this.plugins = plugins; | ||||
| 
 | ||||
| 		const { | ||||
| 			REQUEST, | ||||
|  | @ -50,13 +53,15 @@ class Request { | |||
| 	} | ||||
| 
 | ||||
| 	findDependencies(request, set = new Set()) { | ||||
| 		if (typeof request === 'object') { | ||||
| 			const keys = Object.keys(request).filter(key => key !== 'ALIAS'); | ||||
| 		let type = typeof request; | ||||
| 
 | ||||
| 			keys.forEach(key => { | ||||
| 				set = this.findDependencies(request[key], set); | ||||
| 			}); | ||||
| 		} else if (typeof request === 'string') { | ||||
| 		if (type === 'object') { | ||||
| 			Object.keys(request) | ||||
| 				.filter(key => key !== 'ALIAS') | ||||
| 				.forEach(key => { | ||||
| 					set = this.findDependencies(request[key], set); | ||||
| 				}); | ||||
| 		} else if (type === 'string') { | ||||
| 			const matches = request.match(replacementRegex) || []; | ||||
| 			const deps = matches.map(m => m.split('.')[0].substring(1)); | ||||
| 
 | ||||
|  | @ -66,21 +71,22 @@ class Request { | |||
| 		return set; | ||||
| 	} | ||||
| 
 | ||||
| 	async exec(modifiers = [], cache = new RequestCache()) { | ||||
| 		const settings = { | ||||
| 			endpoint: cache.parse(this.ENDPOINT), | ||||
| 	async exec(cache = new RequestCache()) { | ||||
| 		let settings = cache.parse({ | ||||
| 			endpoint: this.ENDPOINT, | ||||
| 			method: this.VERB, | ||||
| 			headers: cache.parse(this.HEADERS), | ||||
| 			query: cache.parse(this.PARAMS), | ||||
| 			payload: cache.parse(this.PAYLOAD) | ||||
| 		}; | ||||
| 
 | ||||
| 		modifiers.forEach(mod => { | ||||
| 			if (typeof mod.preRequest !== 'undefined') { | ||||
| 				mod.preRequest(settings, this.originalRequest); | ||||
| 			} | ||||
| 			headers: this.HEADERS, | ||||
| 			query: this.PARAMS, | ||||
| 			payload: this.PAYLOAD | ||||
| 		}); | ||||
| 
 | ||||
| 		settings = this.plugins.replaceDynamicValues(settings); | ||||
| 
 | ||||
| 		settings = this.plugins.execPreRequestModifiers( | ||||
| 			settings, | ||||
| 			this.originalRequest | ||||
| 		); | ||||
| 
 | ||||
| 		try { | ||||
| 			const response = await request( | ||||
| 				removeOptionalKeys( | ||||
|  | @ -100,7 +106,7 @@ class Request { | |||
| 				) | ||||
| 			); | ||||
| 
 | ||||
| 			const results = { | ||||
| 			let results = { | ||||
| 				request: { | ||||
| 					headers: response.request.headers, | ||||
| 					body: response.request.body, | ||||
|  | @ -114,11 +120,16 @@ class Request { | |||
| 				body: response.body | ||||
| 			}; | ||||
| 
 | ||||
| 			results = this.plugins.execPostRequestModifiers( | ||||
| 				results, | ||||
| 				this.originalRequest | ||||
| 			); | ||||
| 
 | ||||
| 			cache.add(`$${this.ALIAS}`, results); | ||||
| 
 | ||||
| 			return results; | ||||
| 		} catch ({ error }) { | ||||
| 			throw new Error(error); | ||||
| 			throw new Error(`Request Error: ` + error); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| const { replacementRegex } = require('./shared'); | ||||
| const { replacementRegex, replaceInObject } = require('./shared'); | ||||
| 
 | ||||
| class RequestCache { | ||||
| 	constructor() { | ||||
|  | @ -27,26 +27,13 @@ class RequestCache { | |||
| 	} | ||||
| 
 | ||||
| 	parse(item) { | ||||
| 		let type = typeof item; | ||||
| 
 | ||||
| 		if (type === 'undefined') { | ||||
| 			return {}; | ||||
| 		} | ||||
| 
 | ||||
| 		if (item === null) { | ||||
| 			return null; | ||||
| 		} | ||||
| 
 | ||||
| 		if (type === 'string') { | ||||
| 			return item.replace(replacementRegex, key => this.get(key)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (type === 'object') { | ||||
| 			Object.keys(item).forEach(k => (item[k] = this.parse(item[k]))); | ||||
| 			return item; | ||||
| 		} | ||||
| 
 | ||||
| 		return item; | ||||
| 		return replaceInObject(item, item => | ||||
| 			item.replace(replacementRegex, key => this.get(key)) | ||||
| 		); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,14 +1,12 @@ | |||
| const Request = require('./request'); | ||||
| const RequestCache = require('./requestCache'); | ||||
| const httpVerbs = require('./shared').httpVerbs; | ||||
| const requireg = require('requireg'); | ||||
| 
 | ||||
| class RequestList { | ||||
| 	constructor(requests = [], config = {}) { | ||||
| 	constructor(config = { REQUESTS: [] }) { | ||||
| 		this.config = config; | ||||
| 		this.requests = requests; | ||||
| 		this.REQUESTS = config.REQUESTS; | ||||
| 
 | ||||
| 		this.modifiers = this.loadPlugins(); | ||||
| 		this.list = this.loadRequests(); | ||||
| 		this.cache = new RequestCache(); | ||||
| 
 | ||||
|  | @ -17,7 +15,7 @@ class RequestList { | |||
| 
 | ||||
| 	async execByAlias(alias) { | ||||
| 		if (this.cache.exists(`$${alias}`)) { | ||||
| 			return this.applyPostResponseModifiers(this.cache.get(`$${alias}`)); | ||||
| 			return this.cache.get(`$${alias}`); | ||||
| 		} | ||||
| 
 | ||||
| 		const request = this.list.find(r => r.ALIAS === alias); | ||||
|  | @ -28,9 +26,8 @@ class RequestList { | |||
| 
 | ||||
| 		try { | ||||
| 			await this.fetchDependencies(Array.from(request.DEPENDENCIES)); | ||||
| 			const response = await request.exec(this.modifiers, this.cache); | ||||
| 
 | ||||
| 			return this.applyPostResponseModifiers(response); | ||||
| 			const response = await request.exec(this.cache); | ||||
| 			return response; | ||||
| 		} catch (reason) { | ||||
| 			throw new Error( | ||||
| 				`Request: ${request.VERB} ${ | ||||
|  | @ -49,10 +46,9 @@ class RequestList { | |||
| 
 | ||||
| 	loadRequests() { | ||||
| 		let requests = []; | ||||
| 		this.requests.forEach(request => { | ||||
| 		this.REQUESTS.forEach(request => { | ||||
| 			try { | ||||
| 				let r = new Request(request); | ||||
| 				requests.push(r); | ||||
| 				requests.push(new Request(request, this.config.PLUGINS)); | ||||
| 			} catch (e) { | ||||
| 				throw new Error(`${request.request} was ignored: ${e}`); | ||||
| 			} | ||||
|  | @ -60,34 +56,6 @@ class RequestList { | |||
| 
 | ||||
| 		return requests; | ||||
| 	} | ||||
| 
 | ||||
| 	loadPlugins() { | ||||
| 		if (typeof this.config.PLUGINS === 'undefined') { | ||||
| 			return []; | ||||
| 		} | ||||
| 
 | ||||
| 		return this.config.PLUGINS.map(plugin => { | ||||
| 			let name = plugin; | ||||
| 			let settings = null; | ||||
| 
 | ||||
| 			if (typeof plugin === 'object') { | ||||
| 				name = Object.keys(plugin)[0]; | ||||
| 				settings = plugin[name]; | ||||
| 			} | ||||
| 
 | ||||
| 			return new (requireg(name))(settings); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	applyPostResponseModifiers(response) { | ||||
| 		this.modifiers.forEach(mod => { | ||||
| 			if (typeof mod.postResponse !== 'undefined') { | ||||
| 				mod.postResponse(response); | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		return response; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = RequestList; | ||||
|  |  | |||
|  | @ -11,7 +11,8 @@ const httpVerbs = [ | |||
| ]; | ||||
| 
 | ||||
| const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i'); | ||||
| const replacementRegex = /\$([a-zA-Z\.\d\-\_\/\\\:]*)/g; | ||||
| const replacementRegex = /\$([a-zA-Z\.\d\-\_\/\\\:]+)/g; | ||||
| const dynamicValueRegex = /\$\[(\w+\((?:.|[\n\r])+?\))\]/g; | ||||
| 
 | ||||
| const UpperCaseKeys = function(obj) { | ||||
| 	let result = {}; | ||||
|  | @ -37,10 +38,43 @@ const removeOptionalKeys = function(obj, optionalValues) { | |||
| 	return result; | ||||
| }; | ||||
| 
 | ||||
| const toKebabCase = function(str) { | ||||
| 	return str | ||||
| 		.trim() | ||||
| 		.replace(/([a-z])([A-Z])/g, '$1-$2') | ||||
| 		.replace(/\s+/g, '-') | ||||
| 		.toLowerCase(); | ||||
| }; | ||||
| 
 | ||||
| const replaceInObject = function(obj, fn) { | ||||
| 	if (obj === null) { | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	let type = typeof obj; | ||||
| 
 | ||||
| 	if (type === 'undefined') { | ||||
| 		return {}; | ||||
| 	} | ||||
| 
 | ||||
| 	if (type === 'string') { | ||||
| 		return fn(obj); | ||||
| 	} | ||||
| 
 | ||||
| 	if (type === 'object') { | ||||
| 		Object.keys(obj).forEach(k => (obj[k] = replaceInObject(obj[k], fn))); | ||||
| 	} | ||||
| 
 | ||||
| 	return obj; | ||||
| }; | ||||
| 
 | ||||
| module.exports = { | ||||
| 	httpVerbs, | ||||
| 	requestRegex, | ||||
| 	replacementRegex, | ||||
| 	dynamicValueRegex, | ||||
| 	UpperCaseKeys, | ||||
| 	removeOptionalKeys | ||||
| 	removeOptionalKeys, | ||||
| 	toKebabCase, | ||||
| 	replaceInObject | ||||
| }; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 GitHub
							GitHub