mirror of https://github.com/Seich/Beau.git
				
				
				
			Compare commits
	
		
			182 Commits
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | eee0aa95ea | |
|  | b54d2158df | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 8b72a42af1 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | ac432305b9 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 98c6b4d941 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 1027825091 | |
| ![dependabot[bot]](/assets/img/avatar_default.png) dependabot[bot] | 7e7ae395f9 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | e0de623d1a | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 92485a5277 | |
| ![dependabot[bot]](/assets/img/avatar_default.png) dependabot[bot] | 3175e8c142 | |
|  | 2302763503 | |
|  | fd2cc609ea | |
|  | cdd0c93dee | |
|  | f82a529ebe | |
|  | 3cb6851ead | |
|  | 78596a154d | |
|  | dafd4406c1 | |
|  | 7adba8e3dc | |
|  | 849b823311 | |
|  | 987c1df81e | |
|  | d837be77de | |
|  | 2af8e5492f | |
|  | d6c6c0c7e4 | |
|  | 44919334be | |
|  | a9eb8c97dd | |
|  | fa28dbee8b | |
|  | 9decb4978b | |
|  | 56894bd459 | |
|  | 1e4e0ba885 | |
|  | c73f43aedb | |
|  | b7d2e56692 | |
|  | 3b5e848530 | |
|  | a39f3b5977 | |
|  | 5029a09b41 | |
|  | df41d4fa0b | |
|  | acb39bd21f | |
|  | 614b1bf966 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 162e6f265c | |
|  | b6ef0f06a3 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 938c82125c | |
|  | 2ee372657a | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | e97038c6db | |
|  | acdd458af1 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 09eebcf505 | |
|  | dd556a85e1 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | f4f73e2ca8 | |
|  | 342eef7915 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 8cf3f1cecc | |
|  | 9f1a3532f6 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 7ac883999f | |
|  | 2b69c79e06 | |
| ![dependabot[bot]](/assets/img/avatar_default.png) dependabot[bot] | ef41c5360a | |
|  | 311e56c609 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 95b55edc39 | |
|  | 7261bfe76a | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 15fc4dae8d | |
|  | d54b99226b | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 3eace272e2 | |
|  | 4381e123a3 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 3e18f32c55 | |
|  | b4479a91e7 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 9a04b99366 | |
|  | 4938dc3602 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | d1d08f4d60 | |
|  | 551499f822 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 2dfbc5e4c1 | |
|  | 5328428298 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 1e376e625f | |
|  | 1a526c3869 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 895a6efe90 | |
|  | 64c3d29664 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 39cd4c802e | |
|  | f883e6611b | |
|  | 2c478231a6 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | bf121d6e57 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | f5c09d8415 | |
| ![dependabot-preview[bot]](/assets/img/avatar_default.png) dependabot-preview[bot] | 607c6fcfe4 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 133423c98d | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 57ab2ad9ab | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 4db7f7f5a7 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 1ac4a39cd2 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 63c0d624df | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 0483417465 | |
| ![dependabot[bot]](/assets/img/avatar_default.png) dependabot[bot] | 87316d4f3f | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 5ae23a6882 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 1cfbfd48f7 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | a1b342fd3f | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | cbcf513c86 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | c92da8d251 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 5fd0bf6944 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 9a8d02562f | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 5ae7f88a27 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 7f5c9f8fab | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 455b7676a6 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 19b9bb85cc | |
|  | 4f6078758d | |
|  | ee71f16e56 | |
| ![dependabot[bot]](/assets/img/avatar_default.png) dependabot[bot] | 3e9034a556 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | cb43578d2d | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 1bc2e4cbd4 | |
|  | 3e73ec48db | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | c5bf148d74 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 209ceccc36 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 0a0d2e258b | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | b4355c0750 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 54bafbb4a5 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | c0f28fc5d0 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 1e67cffebf | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 4acb27174d | |
|  | 2610ce5a0d | |
|  | 7854333a50 | |
|  | f7cb19276b | |
|  | b4e27b179b | |
|  | fea72dde2a | |
|  | 94332bd125 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 8a1ecc67bd | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 2961b1ea54 | |
|  | 8b6bbfd77b | |
|  | f0298a973b | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 8c453e6974 | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 865d7976fe | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | 379395bcbd | |
| ![greenkeeper[bot]](/assets/img/avatar_default.png) greenkeeper[bot] | d517d4055e | |
|  | d5da7d87e2 | |
|  | f2b9cad662 | |
|  | cc805d4adb | |
|  | 1814a00835 | |
|  | 24c0bfb04f | |
|  | 52e88a3703 | |
|  | 8849fe1e90 | |
|  | 6be3ce05c2 | |
|  | b6c5a0b01e | |
|  | 14704c09d1 | |
|  | 450d53e9f9 | |
|  | ef34ea04aa | |
|  | cc7245b501 | |
|  | 8e961211a5 | |
|  | c8ff4945d6 | |
|  | 8fcd8b9fd3 | |
|  | 26b33fbf00 | |
|  | 23064040df | |
|  | dbc7addb39 | |
|  | 293dde59c4 | |
|  | a9ba5b0bb7 | |
|  | c0923dcce5 | |
|  | 29940d2a24 | |
|  | aba33e7fc3 | |
|  | d2eabfb19e | |
|  | 0677497074 | |
|  | 9ba3027017 | |
|  | 0e5b427d24 | |
|  | 248f4a9223 | |
|  | 56d536a509 | |
|  | 4bf7a60e9c | |
|  | 59f85fac8c | |
|  | 64de56d51c | |
|  | 3212403962 | |
|  | 90761acaa4 | |
|  | 4fba235bad | |
|  | 91d35459aa | |
|  | 185333cb20 | |
|  | dd2842c097 | |
|  | 92142c148b | |
|  | 358da930e8 | |
|  | 17971a3cca | |
|  | d3b78bc27d | |
|  | 979f93ddbb | |
|  | 9d10db3243 | |
|  | 3df90600ac | |
|  | 6c552cb7a2 | |
|  | d15564a9e3 | |
|  | d154f73232 | |
|  | f13cbda84a | |
|  | e9c07cbbf9 | |
|  | 490bc85174 | |
|  | 1fb45da5de | |
|  | 293c3883e9 | |
|  | 7e25f9fd29 | |
|  | 070ffc3106 | |
|  | 2f33442072 | |
|  | 228d2de7f3 | |
|  | 124d55d1ae | 
|  | @ -1 +1,2 @@ | ||||||
| * text=auto | * text=auto | ||||||
|  | *.js text eol=lf | ||||||
|  |  | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | name: Tests | ||||||
|  | on: | ||||||
|  |     push: | ||||||
|  |         branches: [master] | ||||||
|  | 
 | ||||||
|  |     pull_request: | ||||||
|  |         branches: [master] | ||||||
|  | 
 | ||||||
|  | jobs: | ||||||
|  |     build: | ||||||
|  |         runs-on: ubuntu-latest | ||||||
|  |         steps: | ||||||
|  |             - uses: actions/checkout@v2 | ||||||
|  |             - uses: actions/setup-node@v2-beta | ||||||
|  |             - name: Install dependencies | ||||||
|  |               run: | | ||||||
|  |                   npm install | ||||||
|  |                   wget https://codeclimate.com/downloads/test-reporter/test-reporter-0.6.3-linux-amd64 -O cc-test-reporter | ||||||
|  |                   chmod +x ./cc-test-reporter | ||||||
|  | 
 | ||||||
|  |             - name: Validate Schema | ||||||
|  |               run: | | ||||||
|  |                 npx ajv-cli compile -s schema.json | ||||||
|  |                 npx ajv-cli validate -s schema.json -d examples/beau.yml | ||||||
|  |             - name: Run CLI Tests | ||||||
|  |               run: npm test -- ./bin | ||||||
|  |             - name: Run Lib Tests | ||||||
|  |               run: npm run test:coverage | ||||||
|  |             - name: Report Results | ||||||
|  |               if: success() | ||||||
|  |               run: | | ||||||
|  |                   ./cc-test-reporter format-coverage | ||||||
|  |                   ./cc-test-reporter upload-coverage | ||||||
|  |               env: | ||||||
|  |                 GIT_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} | ||||||
|  |                 GIT_BRANCH: ${{ github.head_ref }} | ||||||
|  |                 CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE_REPO_TOKEN }} | ||||||
|  | @ -1,2 +1,3 @@ | ||||||
| node_modules/ | node_modules/ | ||||||
| coverage/ | coverage/ | ||||||
|  | *.swp | ||||||
|  |  | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | printWidth: 80 | ||||||
|  | tabWidth: 4 | ||||||
|  | singleQuote: true | ||||||
|  | useTabs: false | ||||||
|  | trailingComma: none | ||||||
|  | bracketSpacing: true | ||||||
|  | jsxBracketSameLine: true | ||||||
|  | semi: false | ||||||
|  | requirePragma: false | ||||||
|  | proseWrap: always | ||||||
							
								
								
									
										42
									
								
								LICENSE
								
								
								
								
							
							
						
						
									
										42
									
								
								LICENSE
								
								
								
								
							|  | @ -1,7 +1,41 @@ | ||||||
| Copyright 2018 David Sergio Díaz | “Commons Clause” License Condition v1.0 | ||||||
| 
 | 
 | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | The Software is provided to you by the Licensor under the License, as defined | ||||||
|  | below, subject to the following condition. | ||||||
| 
 | 
 | ||||||
| The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | Without limiting other conditions in the License, the grant of rights under the | ||||||
|  | License will not include, and the License does not grant to you, the right to | ||||||
|  | Sell the Software. | ||||||
| 
 | 
 | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | For purposes of the foregoing, “Sell” means practicing any or all of the rights | ||||||
|  | granted to you under the License to provide to third parties, for a fee or other | ||||||
|  | consideration (including without limitation fees for hosting or consulting/ | ||||||
|  | support services related to the Software), a product or service whose value | ||||||
|  | derives, entirely or substantially, from the functionality of the Software. Any | ||||||
|  | license notice or attribution required by the License must also include this | ||||||
|  | Commons Clause License Condition notice. | ||||||
|  | 
 | ||||||
|  | Software: Beau | ||||||
|  | License: MIT | ||||||
|  | Licensor: David Díaz | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | Copyright 2020 David Díaz | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||||
|  | this software and associated documentation files (the "Software"), to deal in | ||||||
|  | the Software without restriction, including without limitation the rights to | ||||||
|  | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||||
|  | the Software, and to permit persons to whom the Software is furnished to do so, | ||||||
|  | subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||||
|  | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||||
|  | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||||
|  | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||||
|  | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  |  | ||||||
							
								
								
									
										82
									
								
								README.md
								
								
								
								
							
							
						
						
									
										82
									
								
								README.md
								
								
								
								
							|  | @ -1,22 +1,23 @@ | ||||||
| <div align="center"> | <div align="center"> | ||||||
| 	<img src="http://files.martianwabbit.com/beau.png?1" height="144"/> |     <img src="media/beau.png" height="144" alt="Beau's Logo is a Seahorse" /> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| <h1 align="center">Beau</h1> | <h1 align="center">Beau</h1> | ||||||
| <p align="center">Testing JSON APIs made easy.</p> | <p align="center">Testing JSON APIs made easy.</p> | ||||||
| <p align="center"> | <p align="center"> | ||||||
| 	<a href="https://codeclimate.com/github/Seich/Beau/maintainability"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/maintainability" /></a> |     <a href="https://codeclimate.com/github/Seich/Beau/maintainability"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/maintainability" /></a> | ||||||
| 	<a href="https://codeclimate.com/github/Seich/Beau/test_coverage"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/test_coverage" /></a> |     <a href="https://codeclimate.com/github/Seich/Beau/test_coverage"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/test_coverage" /></a> | ||||||
| 	<a href="https://circleci.com/gh/Seich/Beau/tree/master"><img src="https://circleci.com/gh/Seich/Beau/tree/master.svg?style=svg" alt="CircleCI"></a> |     <img src="https://github.com/Seich/Beau/workflows/Tests/badge.svg"/> | ||||||
| </p> | </p> | ||||||
| 
 | 
 | ||||||
| ## What is Beau? | ## What is Beau? | ||||||
| 
 | 
 | ||||||
| Beau, is a CLI that executes HTTP requests based on a YAML configuration file. | Beau is a modern http client. It uses a YAML file as configuration allowing you | ||||||
| This makes testing easy, it allows you to share test requests with others as | to test APIs without having to write lengthy commands. | ||||||
| part of your repo. |  | ||||||
| 
 | 
 | ||||||
|  | <div align="center"> | ||||||
|  |     <img src="media/usage.gif" alt="A gif showing how beau works." /> | ||||||
|  | </div> | ||||||
| 
 | 
 | ||||||
| ## Installation | ## Installation | ||||||
| 
 | 
 | ||||||
|  | @ -24,52 +25,45 @@ part of your repo. | ||||||
| 
 | 
 | ||||||
| ## Usage | ## Usage | ||||||
| 
 | 
 | ||||||
|     ⚡  beau --help |     $ beau [COMMAND] | ||||||
| 
 | 
 | ||||||
|     Usage: beau [options] [command] |     COMMANDS | ||||||
| 
 |       help      display help for beau | ||||||
| 
 |       list      Lists all available requests in the config file. | ||||||
|     Options: |       request   Executes a request by name. | ||||||
| 
 |       validate  Validates the given configuration file against Beau's configuration schema. | ||||||
|     -V, --version  output the version number |  | ||||||
|     -h, --help     output usage information |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     Commands: |  | ||||||
| 
 |  | ||||||
|     request [options] <alias> |  | ||||||
|     list [options] |  | ||||||
| 
 | 
 | ||||||
| ## Example Configuration File | ## Example Configuration File | ||||||
| 
 | 
 | ||||||
|     version: 1 | ```yaml | ||||||
|     endpoint: https://example.com/api/ | endpoint: https://httpbin.org/ | ||||||
| 
 | 
 | ||||||
|     POST /session: | POST /anything: | ||||||
|     	ALIAS: session |     alias: anything | ||||||
|     	PAYLOAD: |     payload: | ||||||
|     		username: seich |         hello: world | ||||||
|     		password: hello01 | ``` | ||||||
| 
 | 
 | ||||||
|     GET /profile | ``` | ||||||
|     	ALIAS: profile | $ beau request anything | ||||||
|     	HEADERS: |  | ||||||
|     		authorization: Bearer $session.response.body.token |  | ||||||
| 
 | 
 | ||||||
|     GET /user/$profile.response.body.id/posts | Status              Endpoint | ||||||
|     	ALIAS: friends | 200                 https://httpbin.org/anything | ||||||
|     	HEADERS: |  | ||||||
|     		authorization: Bearer $session.response.body.token |  | ||||||
|     	PARAMS: |  | ||||||
|     		archived: true |  | ||||||
| 
 | 
 | ||||||
| ## Example Usage | { | ||||||
|  | ... | ||||||
|  |   json: { | ||||||
|  |     hello: "world" | ||||||
|  |   }, | ||||||
|  |   method: "POST", | ||||||
|  |   url: "https://httpbin.org/anything" | ||||||
|  |   ... | ||||||
|  | } | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
|     beau request profile | ## Documentation | ||||||
| 
 | 
 | ||||||
| That would execute the profile request along with it´s dependencies. In this | Visit https://beaujs.com/docs/ for the complete docs. | ||||||
| case, the session request would be made as well since we are using it´s response |  | ||||||
| value as part of our current request. |  | ||||||
| 
 | 
 | ||||||
| ## License | ## License | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										177
									
								
								bin/beau
								
								
								
								
							
							
						
						
									
										177
									
								
								bin/beau
								
								
								
								
							|  | @ -1,176 +1,5 @@ | ||||||
| #!/usr/bin/env node | #!/usr/bin/env node | ||||||
| const program = require('commander'); |  | ||||||
| const process = require('process'); |  | ||||||
| const Beau = require('../src/beau'); |  | ||||||
| const yaml = require('js-yaml'); |  | ||||||
| const fs = require('fs'); |  | ||||||
| const { Line, Spinner } = require('clui'); |  | ||||||
| const clc = require('cli-color'); |  | ||||||
| const jsome = require('jsome'); |  | ||||||
| 
 | 
 | ||||||
| const package = require('../package.json'); | require('@oclif/command') | ||||||
| 
 |     .run() | ||||||
| program.version(package.version); |     .catch(require('@oclif/errors/handle')); | ||||||
| 
 |  | ||||||
| program |  | ||||||
| 	.command('request <alias>') |  | ||||||
| 	.option( |  | ||||||
| 		'-c --config <config>', |  | ||||||
| 		'Specify your request config file. Defaults to beau.yml in the current directory.', |  | ||||||
| 		'beau.yml' |  | ||||||
| 	) |  | ||||||
| 	.option( |  | ||||||
| 		'--verbose', |  | ||||||
| 		'Show all the information available on the current request.', |  | ||||||
| 		false |  | ||||||
| 	) |  | ||||||
| 	.option('--no-format', 'Return the text without any special formatting.') |  | ||||||
| 	.action(async (alias, { config, format, verbose }) => { |  | ||||||
| 		const beau = loadConfig(config); |  | ||||||
| 		let spinner; |  | ||||||
| 
 |  | ||||||
| 		if (format) { |  | ||||||
| 			spinner = new Spinner(clc.yellow(`Requesting: ${alias}`)); |  | ||||||
| 			spinner.start(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		try { |  | ||||||
| 			let res = await beau.requests.execByAlias(alias); |  | ||||||
| 			let { status, headers, body } = res.response; |  | ||||||
| 			let { endpoint } = res.request; |  | ||||||
| 
 |  | ||||||
| 			if (format) { |  | ||||||
| 				spinner.stop(); |  | ||||||
| 
 |  | ||||||
| 				status = status.toString().startsWith(2) |  | ||||||
| 					? clc.green(status) |  | ||||||
| 					: clc.red(status); |  | ||||||
| 
 |  | ||||||
| 				new Line() |  | ||||||
| 					.padding(2) |  | ||||||
| 					.column('Status', 20, [clc.cyan]) |  | ||||||
| 					.column('Endpoint', 20, [clc.cyan]) |  | ||||||
| 					.output(); |  | ||||||
| 
 |  | ||||||
| 				new Line() |  | ||||||
| 					.padding(2) |  | ||||||
| 					.column(status, 20) |  | ||||||
| 					.column(endpoint) |  | ||||||
| 					.output(); |  | ||||||
| 
 |  | ||||||
| 				new Line().output(); |  | ||||||
| 
 |  | ||||||
| 				if (verbose) { |  | ||||||
| 					jsome(res); |  | ||||||
| 				} else { |  | ||||||
| 					jsome(body); |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				console.log(status); |  | ||||||
| 				console.log(endpoint); |  | ||||||
| 				console.log(JSON.stringify(headers)); |  | ||||||
| 				console.log(JSON.stringify(body)); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			process.exit(0); |  | ||||||
| 		} catch (err) { |  | ||||||
| 			new Line().output(); |  | ||||||
| 			console.error(err.message); |  | ||||||
| 			process.exit(1); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| program |  | ||||||
| 	.command('list') |  | ||||||
| 	.option( |  | ||||||
| 		'-c --config <config>', |  | ||||||
| 		'Specify your request config file. Defaults to beau.yml in the current directory.', |  | ||||||
| 		'beau.yml' |  | ||||||
| 	) |  | ||||||
| 	.option('--no-format', 'Return the text without any special formatting.') |  | ||||||
| 	.action(({ config, format }) => { |  | ||||||
| 		const beau = loadConfig(config); |  | ||||||
| 
 |  | ||||||
| 		if (format) { |  | ||||||
| 			new Line() |  | ||||||
| 				.padding(2) |  | ||||||
| 				.column('HTTP Verb', 20, [clc.cyan]) |  | ||||||
| 				.column('Alias', 30, [clc.cyan]) |  | ||||||
| 				.column('Endpoint', 20, [clc.cyan]) |  | ||||||
| 				.output(); |  | ||||||
| 
 |  | ||||||
| 			beau.requests.list.forEach(({ VERB, ALIAS, ENDPOINT }) => |  | ||||||
| 				new Line() |  | ||||||
| 					.padding(2) |  | ||||||
| 					.column(VERB, 20, [clc.yellow]) |  | ||||||
| 					.column(ALIAS, 30, [clc.yellow]) |  | ||||||
| 					.column(ENDPOINT) |  | ||||||
| 					.output() |  | ||||||
| 			); |  | ||||||
| 
 |  | ||||||
| 			new Line().output(); |  | ||||||
| 		} else { |  | ||||||
| 			beau.requests.list.forEach(({ VERB, ALIAS, ENDPOINT }) => { |  | ||||||
| 				console.log(`${VERB}\t${ALIAS}\t${ENDPOINT}`); |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| program |  | ||||||
| 	.command('init') |  | ||||||
| 	.option( |  | ||||||
| 		'-e --endpoint <endpoint>', |  | ||||||
| 		'Allows you to set the default endpoint', |  | ||||||
| 		null |  | ||||||
| 	) |  | ||||||
| 	.action(({ endpoint }) => { |  | ||||||
| 		const newFile = `# Beau.yml |  | ||||||
| 
 |  | ||||||
| version: 1${endpoint === null ? ` |  | ||||||
| # endpoint: http://example.com |  | ||||||
| `  : ` |  | ||||||
| endpoint: ${endpoint} |  | ||||||
| `} |  | ||||||
| 
 |  | ||||||
| # defaults: |  | ||||||
| #   params: |  | ||||||
| #     userId: 25 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # GET /profile: profile |  | ||||||
| 
 |  | ||||||
| # GET /posts: |  | ||||||
| #   alias: posts |  | ||||||
| #   params: |  | ||||||
| #     order: ASC |  | ||||||
| 
 |  | ||||||
| # POST /profile: |  | ||||||
| #   alias: save-profile |  | ||||||
| #   headers: |  | ||||||
| #     authentication: Bearer token |  | ||||||
| #   payload: |  | ||||||
| #     name: David |  | ||||||
| #     lastname: Diaz |  | ||||||
| `; |  | ||||||
| 		if (!fs.existsSync('beau.yml')) { |  | ||||||
| 			fs.writeFileSync('beau.yml', newFile); |  | ||||||
| 			console.info('beau.yml created!'); |  | ||||||
| 		} else { |  | ||||||
| 			console.error('beau.yml already exists.'); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| program.parse(process.argv); |  | ||||||
| 
 |  | ||||||
| if (!program.args.length) program.help(); |  | ||||||
| 
 |  | ||||||
| function loadConfig(configFile) { |  | ||||||
| 	if (!fs.existsSync(configFile)) { |  | ||||||
| 		console.error(`The config file, ${configFile} was not found.`); |  | ||||||
| 		process.exit(1); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	const config = yaml.safeLoad(fs.readFileSync(configFile, 'utf-8')); |  | ||||||
| 	return new Beau(config); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | const Beau = require('../../../src/beau') | ||||||
|  | 
 | ||||||
|  | const original = jest.requireActual('../base') | ||||||
|  | 
 | ||||||
|  | const config = { | ||||||
|  |     environment: { | ||||||
|  |         params: { | ||||||
|  |             name: 'David' | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     endpoint: 'https://example.org', | ||||||
|  |     version: 1, | ||||||
|  |     'GET /anything': { | ||||||
|  |         alias: 'alias', | ||||||
|  |         payload: { | ||||||
|  |             name: '$env.params.name' | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     'GET /status/418': { | ||||||
|  |         alias: 'teapot' | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class Base extends original { | ||||||
|  |     loadConfig(configFile, params = []) { | ||||||
|  |         return new Beau(config, {}) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = Base | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | const fs = jest.genMockFromModule('fs') | ||||||
|  | 
 | ||||||
|  | fs.existsSync = (filename) => filename === 'beau.yml' | ||||||
|  | fs.readFileSync = () => ` | ||||||
|  | version: 1 | ||||||
|  | endpoint: https://example.org/
 | ||||||
|  | 
 | ||||||
|  | GET /anything: | ||||||
|  |   alias: anything | ||||||
|  |   payload: | ||||||
|  |     name: $env.params.name | ||||||
|  | ` | ||||||
|  | 
 | ||||||
|  | module.exports = fs | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||||
|  | 
 | ||||||
|  | exports[`List Command with flags: 1`] = ` | ||||||
|  | Array [ | ||||||
|  |   "  HTTP Verb           Alias                         Endpoint             | ||||||
|  | ", | ||||||
|  |   "  GET                 alias                         https://example.org/anything | ||||||
|  | ", | ||||||
|  |   "  GET                 teapot                        https://example.org/status/418 | ||||||
|  | ", | ||||||
|  |   " | ||||||
|  | ", | ||||||
|  | ] | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | exports[`List Command with flags: 2`] = ` | ||||||
|  | Array [ | ||||||
|  |   "GET	alias	https://example.org/anything | ||||||
|  | ", | ||||||
|  |   "GET	teapot	https://example.org/status/418 | ||||||
|  | ", | ||||||
|  | ] | ||||||
|  | `; | ||||||
|  | @ -0,0 +1,71 @@ | ||||||
|  | // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||||
|  | 
 | ||||||
|  | exports[`Request Command with flags: alias %s %s 1`] = ` | ||||||
|  | Array [ | ||||||
|  |   "", | ||||||
|  |   "  Status              Endpoint             | ||||||
|  | ", | ||||||
|  |   "  200                 https://example.org/anything | ||||||
|  | ", | ||||||
|  |   " | ||||||
|  | ", | ||||||
|  |   "{\\"hello\\": \\"world\\"} | ||||||
|  | ", | ||||||
|  | ] | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | exports[`Request Command with flags: alias --as-json %s 1`] = ` | ||||||
|  | Array [ | ||||||
|  |   "{\\"status\\":200,\\"headers\\":[],\\"body\\":\\"{\\\\\\"hello\\\\\\": \\\\\\"world\\\\\\"}\\"} | ||||||
|  | ", | ||||||
|  | ] | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | exports[`Request Command with flags: alias --as-json --verbose 1`] = ` | ||||||
|  | Array [ | ||||||
|  |   "{\\"request\\":{\\"body\\":{\\"name\\":\\"David\\"},\\"endpoint\\":\\"https://example.org/anything\\"},\\"response\\":{\\"status\\":200,\\"headers\\":[],\\"body\\":\\"{\\\\\\"hello\\\\\\": \\\\\\"world\\\\\\"}\\"},\\"body\\":\\"{\\\\\\"hello\\\\\\": \\\\\\"world\\\\\\"}\\"} | ||||||
|  | ", | ||||||
|  | ] | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | exports[`Request Command with flags: alias --no-format %s 1`] = ` | ||||||
|  | Array [ | ||||||
|  |   "200 | ||||||
|  | ", | ||||||
|  |   "https://example.org/anything | ||||||
|  | ", | ||||||
|  |   "[] | ||||||
|  | ", | ||||||
|  |   "\\"{\\\\\\"hello\\\\\\": \\\\\\"world\\\\\\"}\\" | ||||||
|  | ", | ||||||
|  | ] | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | exports[`Request Command with flags: alias --quiet %s 1`] = `Array []`; | ||||||
|  | 
 | ||||||
|  | exports[`Request Command with flags: alias --verbose %s 1`] = ` | ||||||
|  | Array [ | ||||||
|  |   "", | ||||||
|  |   "  Status              Endpoint             | ||||||
|  | ", | ||||||
|  |   "  200                 https://example.org/anything | ||||||
|  | ", | ||||||
|  |   " | ||||||
|  | ", | ||||||
|  |   "{ | ||||||
|  |   \\"request\\": { | ||||||
|  |     \\"body\\": { | ||||||
|  |       \\"name\\": \\"David\\" | ||||||
|  |     }, | ||||||
|  |     \\"endpoint\\": \\"https://example.org/anything\\" | ||||||
|  |   }, | ||||||
|  |   \\"response\\": { | ||||||
|  |     \\"status\\": 200, | ||||||
|  |     \\"headers\\": [], | ||||||
|  |     \\"body\\": \\"{\\\\\\"hello\\\\\\": \\\\\\"world\\\\\\"}\\" | ||||||
|  |   }, | ||||||
|  |   \\"body\\": \\"{\\\\\\"hello\\\\\\": \\\\\\"world\\\\\\"}\\" | ||||||
|  | } | ||||||
|  | ", | ||||||
|  | ] | ||||||
|  | `; | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | const ListCommand = require('../commands/list') | ||||||
|  | 
 | ||||||
|  | jest.mock('../../../src/shared') | ||||||
|  | 
 | ||||||
|  | jest.mock('../base') | ||||||
|  | 
 | ||||||
|  | describe('List Command', () => { | ||||||
|  |     let result | ||||||
|  | 
 | ||||||
|  |     beforeEach(() => { | ||||||
|  |         result = [] | ||||||
|  |         jest.spyOn(process.stdout, 'write').mockImplementation((val) => | ||||||
|  |             result.push(require('strip-ansi')(val.toString('utf8'))) | ||||||
|  |         ) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     afterEach(() => jest.restoreAllMocks()) | ||||||
|  | 
 | ||||||
|  |     test.each([[], ['--no-format']])('with flags:', async (...args) => { | ||||||
|  |         await ListCommand.run(args) | ||||||
|  |         expect(result).toMatchSnapshot() | ||||||
|  |     }) | ||||||
|  | }) | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | const RequestCommand = require('../commands/request') | ||||||
|  | const requestPromiseNativeMock = require('request-promise-native') | ||||||
|  | 
 | ||||||
|  | jest.mock('../../../src/shared') | ||||||
|  | 
 | ||||||
|  | jest.mock('../base') | ||||||
|  | 
 | ||||||
|  | describe('Request Command', () => { | ||||||
|  |     let result | ||||||
|  | 
 | ||||||
|  |     beforeEach(() => { | ||||||
|  |         requestPromiseNativeMock.fail = false | ||||||
|  |         result = [] | ||||||
|  |         jest.spyOn(process.stdout, 'write').mockImplementation((val) => | ||||||
|  |             result.push(require('strip-ansi')(val.toString('utf8'))) | ||||||
|  |         ) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     afterEach(() => jest.restoreAllMocks()) | ||||||
|  | 
 | ||||||
|  |     test.each([ | ||||||
|  |         ['alias'], | ||||||
|  |         ['alias', '--verbose'], | ||||||
|  |         ['alias', '--as-json'], | ||||||
|  |         ['alias', '--as-json', '--verbose'], | ||||||
|  |         ['alias', '--no-format'], | ||||||
|  |         ['alias', '--quiet'] | ||||||
|  |     ])('with flags: %s %s %s', async (...args) => { | ||||||
|  |         await RequestCommand.run(args) | ||||||
|  |         expect(result).toMatchSnapshot() | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     it('should throw an error when the request fails', async () => { | ||||||
|  |         requestPromiseNativeMock.fail = true | ||||||
|  |         await expect(RequestCommand.run(['anything'])).rejects.toThrow(Error) | ||||||
|  |     }) | ||||||
|  | }) | ||||||
|  | @ -0,0 +1,77 @@ | ||||||
|  | const { Command, flags } = require('@oclif/command') | ||||||
|  | const yaml = require('js-yaml') | ||||||
|  | const fs = require('fs') | ||||||
|  | const path = require('path') | ||||||
|  | const dotenv = require('dotenv') | ||||||
|  | const Beau = require('../../src/beau') | ||||||
|  | const Ajv = require('ajv').default | ||||||
|  | const betterAjvErrors = require('better-ajv-errors') | ||||||
|  | 
 | ||||||
|  | const schema = require('../../schema.json') | ||||||
|  | const ajv = new Ajv() | ||||||
|  | const validate = ajv.compile(schema) | ||||||
|  | 
 | ||||||
|  | class Base extends Command { | ||||||
|  |     openConfigFile(configFile) { | ||||||
|  |         if (!fs.existsSync(configFile)) { | ||||||
|  |             throw new Error(`The config file, ${configFile} was not found.`) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let config | ||||||
|  |         yaml.loadAll(fs.readFileSync(configFile, 'utf-8'), (doc) => { | ||||||
|  |             const valid = validate(doc) | ||||||
|  | 
 | ||||||
|  |             if (!valid) { | ||||||
|  |                 this.log(`The configuration file is not valid.`) | ||||||
|  |                 this.error( | ||||||
|  |                     betterAjvErrors(schema, doc, validate.errors, { indent: 2 }) | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (typeof config === 'undefined') { | ||||||
|  |                 config = doc | ||||||
|  |             } else { | ||||||
|  |                 if (typeof config.hosts === 'undefined') { | ||||||
|  |                     config.hosts = [] | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 config.hosts.push(doc) | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         return config | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     loadConfig(configFile, params = []) { | ||||||
|  |         const config = this.openConfigFile(configFile) | ||||||
|  |         const env = dotenv.config().parsed || {} | ||||||
|  |         params = dotenv.parse(params.join('\n')) | ||||||
|  | 
 | ||||||
|  |         const envParams = { _: Object.assign(env, params) } | ||||||
|  | 
 | ||||||
|  |         const configFileDir = path.dirname( | ||||||
|  |             path.resolve(process.cwd(), configFile) | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         process.chdir(configFileDir) | ||||||
|  | 
 | ||||||
|  |         return new Beau(config, envParams) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Base.flags = { | ||||||
|  |     config: flags.string({ | ||||||
|  |         char: 'c', | ||||||
|  |         description: 'The configuration file to be used.', | ||||||
|  |         default: 'beau.yml' | ||||||
|  |     }), | ||||||
|  |     verbose: flags.boolean({ | ||||||
|  |         char: 'V', | ||||||
|  |         description: `Show all additional information available for a command.` | ||||||
|  |     }), | ||||||
|  |     'no-format': flags.boolean({ | ||||||
|  |         description: `Disables color formatting for usage on external tools.` | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = Base | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | const clc = require('cli-color') | ||||||
|  | const { Line } = require('clui') | ||||||
|  | const { expandPath } = require('../../../src/shared') | ||||||
|  | const Base = require('../base') | ||||||
|  | 
 | ||||||
|  | class ListCommand extends Base { | ||||||
|  |     async run() { | ||||||
|  |         const { flags } = this.parse(ListCommand) | ||||||
|  |         const Beau = this.loadConfig(flags.config) | ||||||
|  | 
 | ||||||
|  |         if (flags['no-format']) { | ||||||
|  |             return Beau.requests.list.forEach( | ||||||
|  |                 ({ VERB, ALIAS, ENDPOINT, PATH }) => | ||||||
|  |                     this.log( | ||||||
|  |                         `${VERB}\t${ALIAS}\t${ENDPOINT.replace( | ||||||
|  |                             /\/$/, | ||||||
|  |                             '' | ||||||
|  |                         )}/${PATH.replace(/^\//, '')}` | ||||||
|  |                     ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         new Line() | ||||||
|  |             .padding(2) | ||||||
|  |             .column('HTTP Verb', 20, [clc.cyan]) | ||||||
|  |             .column('Alias', 30, [clc.cyan]) | ||||||
|  |             .column('Endpoint', 20, [clc.cyan]) | ||||||
|  |             .output() | ||||||
|  | 
 | ||||||
|  |         Beau.requests.list.forEach(({ VERB, ALIAS, ENDPOINT, PATH }) => | ||||||
|  |             new Line() | ||||||
|  |                 .padding(2) | ||||||
|  |                 .column(VERB, 20, [clc.yellow]) | ||||||
|  |                 .column(ALIAS, 30, [clc.yellow]) | ||||||
|  |                 .column(expandPath(ENDPOINT, PATH)) | ||||||
|  |                 .output() | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         new Line().output() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ListCommand.description = `Lists all available requests in the config file.` | ||||||
|  | ListCommand.flags = { ...Base.flags } | ||||||
|  | 
 | ||||||
|  | module.exports = ListCommand | ||||||
|  | @ -0,0 +1,158 @@ | ||||||
|  | const Base = require('../base') | ||||||
|  | const cj = require('color-json') | ||||||
|  | const clc = require('cli-color') | ||||||
|  | const prompts = require('prompts') | ||||||
|  | const { Line, Spinner } = require('clui') | ||||||
|  | const { flags } = require('@oclif/command') | ||||||
|  | const { expandPath } = require('../../../src/shared') | ||||||
|  | 
 | ||||||
|  | class RequestCommand extends Base { | ||||||
|  |     prettyOutput(res, verbose = false) { | ||||||
|  |         let { status, body } = res.response | ||||||
|  | 
 | ||||||
|  |         this.spinner.stop() | ||||||
|  | 
 | ||||||
|  |         status = status.toString().startsWith(2) | ||||||
|  |             ? clc.green(status) | ||||||
|  |             : clc.red(status) | ||||||
|  | 
 | ||||||
|  |         new Line() | ||||||
|  |             .padding(2) | ||||||
|  |             .column('Status', 20, [clc.cyan]) | ||||||
|  |             .column('Endpoint', 20, [clc.cyan]) | ||||||
|  |             .output() | ||||||
|  | 
 | ||||||
|  |         new Line() | ||||||
|  |             .padding(2) | ||||||
|  |             .column(status, 20) | ||||||
|  |             .column(res.request.endpoint) | ||||||
|  |             .output() | ||||||
|  | 
 | ||||||
|  |         new Line().output() | ||||||
|  | 
 | ||||||
|  |         const result = (verbose ? res : body) || null | ||||||
|  |         if (typeof result === 'object') { | ||||||
|  |             this.log(cj(result)) | ||||||
|  |         } else if (typeof result === 'string') { | ||||||
|  |             this.log(result) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async run() { | ||||||
|  |         const { | ||||||
|  |             flags: { | ||||||
|  |                 param: params, | ||||||
|  |                 config, | ||||||
|  |                 'no-format': noFormat = false, | ||||||
|  |                 verbose = false, | ||||||
|  |                 'as-json': asJson = false, | ||||||
|  |                 quiet = false, | ||||||
|  |                 interactive = false | ||||||
|  |             }, | ||||||
|  |             args | ||||||
|  |         } = this.parse(RequestCommand) | ||||||
|  | 
 | ||||||
|  |         const Beau = this.loadConfig(config, params) | ||||||
|  | 
 | ||||||
|  |         const spinnerSprite = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'] | ||||||
|  |         this.spinner = new Spinner('', spinnerSprite) | ||||||
|  | 
 | ||||||
|  |         let spinnerEnabled = !noFormat && !asJson && !quiet | ||||||
|  | 
 | ||||||
|  |         if (typeof args.alias == 'undefined' && !interactive) { | ||||||
|  |             this.error( | ||||||
|  |                 'Missing 1 required argument: The alias of the request to execute.' | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (interactive) { | ||||||
|  |             const requests = Beau.requests.list.map( | ||||||
|  |                 ({ VERB, ALIAS, ENDPOINT, PATH }) => ({ | ||||||
|  |                     title: `${VERB} ${PATH} - ${ALIAS}`, | ||||||
|  |                     value: ALIAS, | ||||||
|  |                     description: expandPath(ENDPOINT, PATH) | ||||||
|  |                 }) | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             const { name } = await prompts({ | ||||||
|  |                 name: 'name', | ||||||
|  |                 message: 'Pick a Request to execute', | ||||||
|  |                 type: 'select', | ||||||
|  |                 choices: requests | ||||||
|  |             }) | ||||||
|  | 
 | ||||||
|  |             args.alias = name | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (spinnerEnabled) { | ||||||
|  |             this.spinner.start() | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let res | ||||||
|  |         try { | ||||||
|  |             res = await Beau.requests.execByAlias(args.alias) | ||||||
|  |         } catch (err) { | ||||||
|  |             this.spinner.stop() | ||||||
|  | 
 | ||||||
|  |             if (!quiet) { | ||||||
|  |                 this.error(err.message) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.exit(1) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (quiet) { | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (asJson) { | ||||||
|  |             return this.log(JSON.stringify(verbose ? res : res.response)) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (noFormat) { | ||||||
|  |             this.log(res.response.status) | ||||||
|  |             this.log(res.request.endpoint) | ||||||
|  |             this.log(JSON.stringify(res.response.headers)) | ||||||
|  |             this.log(JSON.stringify(res.response.body)) | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.prettyOutput(res, verbose) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RequestCommand.description = `Executes a request by name.` | ||||||
|  | RequestCommand.flags = { | ||||||
|  |     ...Base.flags, | ||||||
|  |     param: flags.string({ | ||||||
|  |         char: 'P', | ||||||
|  |         multiple: true, | ||||||
|  |         default: [], | ||||||
|  |         description: `Allows you to inject values into the request's environment.` | ||||||
|  |     }), | ||||||
|  | 
 | ||||||
|  |     quiet: flags.boolean({ | ||||||
|  |         description: `Skips the output.` | ||||||
|  |     }), | ||||||
|  | 
 | ||||||
|  |     'as-json': flags.boolean({ | ||||||
|  |         char: 'j', | ||||||
|  |         description: `Outputs the response as json.` | ||||||
|  |     }), | ||||||
|  | 
 | ||||||
|  |     interactive: flags.boolean({ | ||||||
|  |         char: 'i', | ||||||
|  |         description: 'Choose request interactively.', | ||||||
|  |         default: false | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RequestCommand.args = [ | ||||||
|  |     { | ||||||
|  |         name: 'alias', | ||||||
|  |         required: false, | ||||||
|  |         description: `The alias of the request to execute.` | ||||||
|  |     } | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | module.exports = RequestCommand | ||||||
							
								
								
									
										12
									
								
								circle.yml
								
								
								
								
							
							
						
						
									
										12
									
								
								circle.yml
								
								
								
								
							|  | @ -1,12 +0,0 @@ | ||||||
| machine: |  | ||||||
|   node: |  | ||||||
|     version: 8.9.3 |  | ||||||
| 
 |  | ||||||
| dependencies: |  | ||||||
|   post: |  | ||||||
|     - npm install -g codeclimate-test-reporter |  | ||||||
| 
 |  | ||||||
| test: |  | ||||||
|   pre: |  | ||||||
|     - npm run test:coverage |  | ||||||
|     - codeclimate-test-reporter < ./coverage/lcov.info |  | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | endpoint: https://pokeapi.co/api/v2/ | ||||||
|  | 
 | ||||||
|  | # Try replacing this pokemon using params: | ||||||
|  | # beau request get-pokemon -P "pokemon=dragapult" | ||||||
|  | environment: | ||||||
|  |     _: | ||||||
|  |         pokemon: ditto | ||||||
|  | 
 | ||||||
|  | cookiejar: true | ||||||
|  | 
 | ||||||
|  | GET /pokemon/$env._.pokemon: get-pokemon | ||||||
|  | GET $get-pokemon.body.location_area_encounters: get-encounters | ||||||
|  | 
 | ||||||
|  | POST https://httpbin.org/anything: | ||||||
|  |     - alias: post-first-area | ||||||
|  |       payload: | ||||||
|  |           area: $get-encounters.body.0.location_area.name | ||||||
|  | 
 | ||||||
|  |     - alias: post-pokemon-type | ||||||
|  |       payload: | ||||||
|  |           type: $get-pokemon.body.types.0.type.name | ||||||
|  | 
 | ||||||
|  |     - alias: form-submission | ||||||
|  |       form: | ||||||
|  |           name: Dragapult | ||||||
|  | 
 | ||||||
|  |     - alias: file-upload | ||||||
|  |       formdata: | ||||||
|  |           name: Beau | ||||||
|  |           logo: $[createReadStream('../media/beau.png')] | ||||||
|  | 
 | ||||||
|  | GET https://httpbin.org/status/418: teapot | ||||||
|  | 
 | ||||||
|  | GET https://httpbin.org/cookies/set: | ||||||
|  |     alias: set-cookies | ||||||
|  |     params: | ||||||
|  |         hello: World | ||||||
|  | @ -1,15 +0,0 @@ | ||||||
| VERSION: 1 |  | ||||||
| ENDPOINT: https://api.github.com |  | ||||||
| 
 |  | ||||||
| auth: &auth |  | ||||||
|   HEADERS: |  | ||||||
|     Authorization: token asfdasf123423sd1fgnh7d83n478 |  | ||||||
|     User-Agent: Beau |  | ||||||
| 
 |  | ||||||
| GET /user: |  | ||||||
|   ALIAS: $user |  | ||||||
|   <<: *auth |  | ||||||
| 
 |  | ||||||
| GET /user/repos: |  | ||||||
|   ALIAS: $repos |  | ||||||
|   <<: *auth |  | ||||||
|  | @ -1,32 +0,0 @@ | ||||||
| version: 1 |  | ||||||
| 
 |  | ||||||
| environment: |  | ||||||
|   the: |  | ||||||
|     post: 2 |  | ||||||
| 
 |  | ||||||
| defaults: |  | ||||||
|   headers: |  | ||||||
|     hello: 'Hello2' |  | ||||||
| 
 |  | ||||||
| GET http://jsonplaceholder.typicode.com/posts/1: |  | ||||||
|   alias: a-post |  | ||||||
|   headers: |  | ||||||
|     hello: $jpa2:get-post.body.id |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| hosts: |  | ||||||
|   - host: jpa |  | ||||||
|     endpoint: http://jsonplaceholder.typicode.com |  | ||||||
|     GET /posts/$env.the.post: get-post |  | ||||||
|     GET /users/$jpa:get-post.body.userId: hello |  | ||||||
| 
 |  | ||||||
|   - host: jpa2 |  | ||||||
|     endpoint: http://jsonplaceholder.typicode.com |  | ||||||
|     defaults: |  | ||||||
|       headers: false |  | ||||||
| 
 |  | ||||||
|     GET /posts/$jpa:get-post.body.id: |  | ||||||
|       alias: get-post |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| version: 1 |  | ||||||
| endpoint: 'http://jsonplaceholder.typicode.com' |  | ||||||
| 
 |  | ||||||
| environment: |  | ||||||
|   the: |  | ||||||
|     post: 1 |  | ||||||
| 
 |  | ||||||
| defaults: |  | ||||||
|   headers: |  | ||||||
|     hello: $posts.body.0.userId |  | ||||||
| 
 |  | ||||||
| GET /posts/$env.the.post: get-post |  | ||||||
| 
 |  | ||||||
| GET /posts/: |  | ||||||
|   alias: posts |  | ||||||
|   headers: |  | ||||||
|     hello: false |  | ||||||
| 
 |  | ||||||
| POST /posts/: |  | ||||||
|   alias: new-post |  | ||||||
|   documentation: |  | ||||||
|     title: New Post |  | ||||||
| 
 |  | ||||||
| GET /users/$posts.body.0.userId: |  | ||||||
|   alias: post-user |  | ||||||
|   documentation: |  | ||||||
|     description: Fetches the user for a give post. |  | ||||||
|   params: |  | ||||||
|     hello: 'world' |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| endpoint: http://localhost:10080 |  | ||||||
| 
 |  | ||||||
| plugins: |  | ||||||
|   - beau-jwt: |  | ||||||
|       data: |  | ||||||
|         userId: 12 |  | ||||||
|         name: Sergio |  | ||||||
|       secret: 'asdfasdf+asdfasdf/asdfasdfasdfasdf==' |  | ||||||
| 
 |  | ||||||
| GET /test: |  | ||||||
|   alias: test |  | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| VERSION: '1' |  | ||||||
| ENDPOINT: https://slack.com/api |  | ||||||
| 
 |  | ||||||
| auth: &auth |  | ||||||
|     token: xoxp-139455775026-139455775090-147751461120-f224ed6ffee029869a0f138d0859e7d6 |  | ||||||
| 
 |  | ||||||
| GET /users.getPresence: |  | ||||||
|   ALIAS: presence |  | ||||||
|   PARAMS: |  | ||||||
|     <<: *auth |  | ||||||
| 
 |  | ||||||
| GET /channels.list: |  | ||||||
|   ALIAS: channel-list |  | ||||||
|   PARAMS: |  | ||||||
|     <<: *auth |  | ||||||
|     exclude_archived: true |  | ||||||
| 
 |  | ||||||
| GET /channels.info: |  | ||||||
|   ALIAS: channel-info |  | ||||||
|   PARAMS: |  | ||||||
|     <<: *auth |  | ||||||
|     channel: $channel-list.response.body.channels.0.id |  | ||||||
| 
 |  | ||||||
| POST /chat.postMessage: |  | ||||||
|   ALIAS: new-message |  | ||||||
|   PARAMS: |  | ||||||
|     <<: *auth |  | ||||||
|     channel: $channel-info.response.body.channel.id |  | ||||||
|     text: 'Hey Seich!' |  | ||||||
|     parse: full |  | ||||||
|     link_names: true |  | ||||||
|     username: Beau |  | ||||||
| 
 |  | ||||||
| GET /users.list: |  | ||||||
|   ALIAS: user-list |  | ||||||
|   PARAMS: |  | ||||||
|     <<: *auth |  | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 13 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 616 KiB | 
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										101
									
								
								package.json
								
								
								
								
							
							
						
						
									
										101
									
								
								package.json
								
								
								
								
							|  | @ -1,38 +1,67 @@ | ||||||
| { | { | ||||||
|   "name": "beau", |     "name": "beau", | ||||||
|   "version": "0.7.0", |     "version": "0.11.3", | ||||||
|   "description": "A tool for testing JSON APIs", |     "description": "Testing APIs made easy.", | ||||||
|   "main": "./src/beau.js", |     "main": "./src/beau.js", | ||||||
|   "author": "Sergio Diaz <seich@martianwabbit.com>", |     "author": "David Díaz <seich@martianwabbit.com>", | ||||||
|   "license": "MIT", |     "license": "MIT + Commons Clause", | ||||||
|   "scripts": { |     "scripts": { | ||||||
|     "test": "jest", |         "test": "jest -i", | ||||||
|     "watch": "jest --watch", |         "test:coverage": "jest --coverage ./src", | ||||||
|     "test:coverage": "jest --coverage" |         "release": "np" | ||||||
|   }, |     }, | ||||||
|   "dependencies": { |     "files": [ | ||||||
|     "cli-color": "^1.1.0", |         "/src", | ||||||
|     "clui": "^0.3.1", |         "/bin" | ||||||
|     "commander": "^2.12.2", |     ], | ||||||
|     "deepmerge": "^2.0.1", |     "dependencies": { | ||||||
|     "js-yaml": "^3.7.0", |         "@oclif/command": "1.8.0", | ||||||
|     "jsome": "^2.3.26", |         "@oclif/config": "1.17.0", | ||||||
|     "request": "^2.83.0", |         "@oclif/plugin-help": "3.2.1", | ||||||
|     "request-promise-native": "^1.0.5", |         "@oclif/plugin-warn-if-update-available": "1.7.0", | ||||||
|     "requireg": "^0.1.6" |         "ajv": "7.0.3", | ||||||
|   }, |         "beau-std": "0.9.4", | ||||||
|   "repository": "git@github.com:Seich/Beau.git", |         "better-ajv-errors": "0.7.0", | ||||||
|   "devDependencies": { |         "cli-color": "2.0.0", | ||||||
|     "jest": "22.0.4" |         "clui": "0.3.6", | ||||||
|   }, |         "color-json": "2.0.1", | ||||||
|   "jest": { |         "deepmerge": "4.2.2", | ||||||
|     "testEnvironment": "node", |         "dotenv": "8.2.0", | ||||||
|     "notify": true |         "globby": "11.0.2", | ||||||
|   }, |         "is-plain-object": "5.0.0", | ||||||
|   "bin": { |         "js-yaml": "4.0.0", | ||||||
|     "beau": "./bin/beau" |         "prompts": "2.4.0", | ||||||
|   }, |         "request": "2.88.2", | ||||||
|   "engines": { |         "request-promise-native": "1.0.9", | ||||||
|     "node": ">=8.9.3" |         "requireg": "0.2.2" | ||||||
|   } |     }, | ||||||
|  |     "repository": "git@github.com:Seich/Beau.git", | ||||||
|  |     "devDependencies": { | ||||||
|  |         "jest": "26.6.3", | ||||||
|  |         "jest-watch-typeahead": "0.6.1", | ||||||
|  |         "strip-ansi": "6.0.0", | ||||||
|  |         "np": "7.2.0" | ||||||
|  |     }, | ||||||
|  |     "oclif": { | ||||||
|  |         "commands": "./bin/cli/commands", | ||||||
|  |         "bin": "beau", | ||||||
|  |         "plugins": [ | ||||||
|  |             "@oclif/plugin-help", | ||||||
|  |             "@oclif/plugin-warn-if-update-available" | ||||||
|  |         ] | ||||||
|  |     }, | ||||||
|  |     "jest": { | ||||||
|  |         "testEnvironment": "node", | ||||||
|  |         "notify": true, | ||||||
|  |         "watchPlugins": [ | ||||||
|  |             "jest-watch-typeahead/filename", | ||||||
|  |             "jest-watch-typeahead/testname" | ||||||
|  |         ] | ||||||
|  |     }, | ||||||
|  |     "bin": { | ||||||
|  |         "beau": "./bin/beau" | ||||||
|  |     }, | ||||||
|  |     "engines": { | ||||||
|  |         "node": ">=8.10.0" | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,122 @@ | ||||||
|  | { | ||||||
|  |     "$schema": "http://json-schema.org/draft-07/schema", | ||||||
|  |     "$id": "https://beaujs.com/schema.json", | ||||||
|  |     "title": "Beaujs Requests Schema", | ||||||
|  |     "type": "object", | ||||||
|  |     "definitions": { | ||||||
|  |         "request": { | ||||||
|  |             "oneOf": [ | ||||||
|  |                 { "type": "string" }, | ||||||
|  |                 { | ||||||
|  |                     "type": "object", | ||||||
|  |                     "$ref": "#/definitions/requestObject", | ||||||
|  |                     "required": ["alias"] | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|  |         }, | ||||||
|  |         "requestObject": { | ||||||
|  |             "type": "object", | ||||||
|  |             "properties": { | ||||||
|  |                 "alias": { | ||||||
|  |                     "type": "string", | ||||||
|  |                     "description": "The name of this request." | ||||||
|  |                 }, | ||||||
|  |                 "headers": { | ||||||
|  |                     "type": "object", | ||||||
|  |                     "additionalProperties": true, | ||||||
|  |                     "description": "Headers that are part of this request." | ||||||
|  |                 }, | ||||||
|  |                 "params": { | ||||||
|  |                     "type": "object", | ||||||
|  |                     "additionalProperties": true, | ||||||
|  |                     "description": "Query String parameters to add to this request." | ||||||
|  |                 }, | ||||||
|  |                 "payload": { | ||||||
|  |                     "description": "This request's body. It is converted to json automatically if given an object. It's sent as a string otherwise.", | ||||||
|  |                     "oneOf": [ | ||||||
|  |                         { "type": "string" }, | ||||||
|  |                         { | ||||||
|  |                             "type": "object", | ||||||
|  |                             "additionalProperties": true | ||||||
|  |                         } | ||||||
|  |                     ] | ||||||
|  |                 }, | ||||||
|  |                 "form": { | ||||||
|  |                     "type": "object", | ||||||
|  |                     "additionalProperties": true, | ||||||
|  |                     "description": "This request's body. Sets the content-type to application/x-www-form-urlencoded." | ||||||
|  |                 }, | ||||||
|  |                 "formdata": { | ||||||
|  |                     "type": "object", | ||||||
|  |                     "additionalProperties": true, | ||||||
|  |                     "description": "This request's body. Sets the content-type to multipart/form-data." | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             "allOf": [ | ||||||
|  |                 { "not": { "required": ["payload", "form"] } }, | ||||||
|  |                 { "not": { "required": ["payload", "formdata"] } }, | ||||||
|  |                 { "not": { "required": ["formdata", "form"] } } | ||||||
|  |             ] | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "properties": { | ||||||
|  |         "version": { | ||||||
|  |             "type": "number", | ||||||
|  |             "description": "The beau version this document was created for.", | ||||||
|  |             "enum": [1] | ||||||
|  |         }, | ||||||
|  |         "endpoint": { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "The root endpoint for this host." | ||||||
|  |         }, | ||||||
|  |         "cookiejar": { | ||||||
|  |             "type": "boolean", | ||||||
|  |             "description": "Enable cookie support for requests?" | ||||||
|  |         }, | ||||||
|  |         "host": { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "The name of the current host. It allows referencing requests between different hosts." | ||||||
|  |         }, | ||||||
|  |         "defaults": { | ||||||
|  |             "description": "Default values to be added to all requests.", | ||||||
|  |             "$ref": "#/definitions/requestObject" | ||||||
|  |         }, | ||||||
|  |         "plugins": { | ||||||
|  |             "description": "Plugins to be enabled for this document.", | ||||||
|  |             "type": "array", | ||||||
|  |             "items": { | ||||||
|  |                 "anyOf": [ | ||||||
|  |                     { "type": "string" }, | ||||||
|  |                     { "type": "object", "additionalProperties": true } | ||||||
|  |                 ] | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "environment": { | ||||||
|  |             "type": "object", | ||||||
|  |             "description": "Global document variables for easy access.", | ||||||
|  |             "additionalProperties": true, | ||||||
|  |             "properties": { | ||||||
|  |                 "_": { | ||||||
|  |                     "type": "object", | ||||||
|  |                     "description": "Environment variables brought in by cli params or dotenv.", | ||||||
|  |                     "additionalProperties": true | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "patternProperties": { | ||||||
|  |         "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH)\\s.*": { | ||||||
|  |             "oneOf": [ | ||||||
|  |                 { | ||||||
|  |                     "$ref": "#/definitions/request" | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     "type": "array", | ||||||
|  |                     "items": { | ||||||
|  |                         "$ref": "#/definitions/request" | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -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,20 @@ | ||||||
|  | class Modifiers { | ||||||
|  |     constructor(registry, settings = {}) { | ||||||
|  |         registry.addPreRequestModifier(this.preRequest) | ||||||
|  |         registry.addPostRequestModifier(this.postRequest) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     preRequest(request, orig) { | ||||||
|  |         request.headers = request.headers || {} | ||||||
|  |         request.headers.preRequestModifier = true | ||||||
|  |         return request | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     postRequest(response, orig) { | ||||||
|  |         response.body = 'Hello World' | ||||||
|  |         response.response.body = 'Hello World' | ||||||
|  |         return response | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = Modifiers | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | class BeauStd { | ||||||
|  |     constructor(registry, settings) { | ||||||
|  |         registry.defineDynamicValue('createReadStream', () => {}) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = BeauStd | ||||||
|  | @ -1,22 +1,22 @@ | ||||||
| function Request(request) { | function Request(request) { | ||||||
| 	if (Request.fail) { |     if (Request.fail) { | ||||||
| 		throw new Error(); |         throw new Error() | ||||||
| 	} |     } | ||||||
| 
 | 
 | ||||||
| 	return { |     return { | ||||||
| 		request: { |         request: { | ||||||
| 			headers: request.headers, |             headers: request.headers, | ||||||
| 			body: request.body, |             body: request.body, | ||||||
| 			uri: { |             uri: { | ||||||
| 				href: request.url |                 href: `${request.baseUrl}${request.uri}` | ||||||
| 			} |             } | ||||||
| 		}, |         }, | ||||||
| 		statusCode: 200, |         statusCode: 200, | ||||||
| 		headers: [], |         headers: [], | ||||||
| 		body: '{"hello": "world"}' |         body: '{"hello": "world"}' | ||||||
| 	}; |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Request.fail = false; | Request.fail = false | ||||||
| 
 | 
 | ||||||
| module.exports = Request; | module.exports = Request | ||||||
|  |  | ||||||
|  | @ -1,19 +1,15 @@ | ||||||
| module.exports = function(name) { | function requireg(name) { | ||||||
| 	return function(settings) { |     return require(name) | ||||||
| 		let response = { | } | ||||||
| 			name: `${name}`, |  | ||||||
| 			preRequest(request) { |  | ||||||
| 				request.wasModified = true; |  | ||||||
| 
 | 
 | ||||||
| 				return request; | requireg.resolving = true | ||||||
| 			}, |  | ||||||
| 
 | 
 | ||||||
| 			postResponse(response) { | requireg.resolve = function (name) { | ||||||
| 				response.changed = true; |     if (requireg.resolving) { | ||||||
| 				return response; |         return '' | ||||||
| 			} |     } else { | ||||||
| 		}; |         return undefined | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 		return response; | module.exports = requireg | ||||||
| 	}; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | module.exports = { | ||||||
|  |     ...jest.requireActual('../shared'), | ||||||
|  |     moduleVersion: jest.fn().mockReturnValue(1) | ||||||
|  | } | ||||||
|  | @ -1,207 +1,117 @@ | ||||||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||||
| 
 | 
 | ||||||
| exports[`Beau's config Loader. should create a request list 1`] = ` | exports[`Beau's config Loader. should load the config 1`] = ` | ||||||
| Beau { | Beau { | ||||||
|   "config": Config { |   "config": Config { | ||||||
|     "CACHE": false, |     "COOKIEJAR": false, | ||||||
|     "DEFAULTS": Object { |     "DEFAULTS": Object { | ||||||
|       "headers": Object { |       "headers": Object { | ||||||
|         "authentication": "hello", |         "authentication": "hello", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     "ENDPOINT": "http://jsonplaceholder.typicode.com", |     "ENDPOINT": "http://example.com", | ||||||
|     "ENVIRONMENT": Object {}, |     "ENVIRONMENT": Object {}, | ||||||
|     "HOSTS": Array [], |     "HOSTS": Array [], | ||||||
|     "PLUGINS": Array [], |     "PLUGINS": Plugins { | ||||||
|  |       "autoload": Array [ | ||||||
|  |         "std", | ||||||
|  |       ], | ||||||
|  |       "context": Object {}, | ||||||
|  |       "registry": Object { | ||||||
|  |         "dynamicValues": Array [], | ||||||
|  |         "postRequestModifiers": Array [], | ||||||
|  |         "preRequestModifiers": Array [], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "REQUESTS": Array [], | ||||||
|     "VERSION": 1, |     "VERSION": 1, | ||||||
|     "configKeys": Array [ |     "configKeys": Array [ | ||||||
|       "VERSION", |       "VERSION", | ||||||
|       "CACHE", |  | ||||||
|       "ENDPOINT", |       "ENDPOINT", | ||||||
|       "PLUGINS", |       "PLUGINS", | ||||||
|       "DEFAULTS", |       "DEFAULTS", | ||||||
|       "ENVIRONMENT", |       "ENVIRONMENT", | ||||||
|       "HOSTS", |       "HOSTS", | ||||||
|     ], |       "COOKIEJAR", | ||||||
|     "defaultConfigValues": Object { |  | ||||||
|       "CACHE": false, |  | ||||||
|       "DEFAULTS": Object {}, |  | ||||||
|       "ENDPOINT": "", |  | ||||||
|       "ENVIRONMENT": Object {}, |  | ||||||
|       "HOSTS": Array [], |  | ||||||
|       "PLUGINS": Array [], |  | ||||||
|       "VERSION": 1, |  | ||||||
|     }, |  | ||||||
|     "doc": Object { |  | ||||||
|       "GET /posts/1": "get-post", |  | ||||||
|       "GET /user": Object { |  | ||||||
|         "alias": "user", |  | ||||||
|         "headers": Object { |  | ||||||
|           "hello": "world", |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|       "defaults": Object { |  | ||||||
|         "headers": Object { |  | ||||||
|           "authentication": "hello", |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|       "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", |  | ||||||
|       }, |  | ||||||
|     ], |     ], | ||||||
|   }, |   }, | ||||||
|   "requests": RequestList { |   "requests": RequestList { | ||||||
|     "cache": RequestCache { |     "cache": RequestCache { | ||||||
|       "$cache": Object { |       "$cache": Object { | ||||||
|         "$env": Object {}, |         "env": Object {}, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     "config": Config { |     "list": Array [], | ||||||
|       "CACHE": false, |   }, | ||||||
|       "DEFAULTS": Object { | } | ||||||
|         "headers": Object { | `; | ||||||
|           "authentication": "hello", | 
 | ||||||
|         }, | exports[`Beau's config Loader. should load the request list using the configuration 1`] = ` | ||||||
|       }, | RequestList { | ||||||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", |   "cache": RequestCache { | ||||||
|       "ENVIRONMENT": Object {}, |     "$cache": Object { | ||||||
|       "HOSTS": Array [], |       "env": Object {}, | ||||||
|       "PLUGINS": Array [], |  | ||||||
|       "VERSION": 1, |  | ||||||
|       "configKeys": Array [ |  | ||||||
|         "VERSION", |  | ||||||
|         "CACHE", |  | ||||||
|         "ENDPOINT", |  | ||||||
|         "PLUGINS", |  | ||||||
|         "DEFAULTS", |  | ||||||
|         "ENVIRONMENT", |  | ||||||
|         "HOSTS", |  | ||||||
|       ], |  | ||||||
|       "defaultConfigValues": Object { |  | ||||||
|         "CACHE": false, |  | ||||||
|         "DEFAULTS": Object {}, |  | ||||||
|         "ENDPOINT": "", |  | ||||||
|         "ENVIRONMENT": Object {}, |  | ||||||
|         "HOSTS": Array [], |  | ||||||
|         "PLUGINS": Array [], |  | ||||||
|         "VERSION": 1, |  | ||||||
|       }, |  | ||||||
|       "doc": Object { |  | ||||||
|         "GET /posts/1": "get-post", |  | ||||||
|         "GET /user": Object { |  | ||||||
|           "alias": "user", |  | ||||||
|           "headers": Object { |  | ||||||
|             "hello": "world", |  | ||||||
|           }, |  | ||||||
|         }, |  | ||||||
|         "defaults": Object { |  | ||||||
|           "headers": Object { |  | ||||||
|             "authentication": "hello", |  | ||||||
|           }, |  | ||||||
|         }, |  | ||||||
|         "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 { |   "list": Array [ | ||||||
|  |     Request { | ||||||
|  |       "ALIAS": "get-post", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|  |       "DEPENDENCIES": Set {}, | ||||||
|  |       "ENDPOINT": "http://example.com", | ||||||
|  |       "PATH": "/posts/1", | ||||||
|  |       "REQUEST": "GET /posts/1", | ||||||
|  |       "VERB": "GET", | ||||||
|  |       "originalRequest": Object { | ||||||
|         "ALIAS": "get-post", |         "ALIAS": "get-post", | ||||||
|         "DEPENDENCIES": Set {}, |         "COOKIEJAR": false, | ||||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com/posts/1", |         "ENDPOINT": "http://example.com", | ||||||
|         "HEADERS": Object { |  | ||||||
|           "authentication": "hello", |  | ||||||
|         }, |  | ||||||
|         "PARAMS": undefined, |  | ||||||
|         "PAYLOAD": undefined, |  | ||||||
|         "VERB": "GET", |  | ||||||
|         "originalRequest": Object { |  | ||||||
|           "ALIAS": "get-post", |  | ||||||
|           "ENDPOINT": "http://jsonplaceholder.typicode.com", |  | ||||||
|           "HEADERS": Object { |  | ||||||
|             "authentication": "hello", |  | ||||||
|           }, |  | ||||||
|           "REQUEST": "GET /posts/1", |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|       Request { |  | ||||||
|         "ALIAS": "user", |  | ||||||
|         "DEPENDENCIES": Set {}, |  | ||||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com/user", |  | ||||||
|         "HEADERS": Object { |  | ||||||
|           "authentication": "hello", |  | ||||||
|           "hello": "world", |  | ||||||
|         }, |  | ||||||
|         "PARAMS": undefined, |  | ||||||
|         "PAYLOAD": undefined, |  | ||||||
|         "VERB": "GET", |  | ||||||
|         "originalRequest": Object { |  | ||||||
|           "ALIAS": "user", |  | ||||||
|           "ENDPOINT": "http://jsonplaceholder.typicode.com", |  | ||||||
|           "HEADERS": Object { |  | ||||||
|             "authentication": "hello", |  | ||||||
|             "hello": "world", |  | ||||||
|           }, |  | ||||||
|           "REQUEST": "GET /user", |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|     ], |  | ||||||
|     "modifiers": Array [], |  | ||||||
|     "requests": Array [ |  | ||||||
|       Object { |  | ||||||
|         "ALIAS": "get-post", |  | ||||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com", |  | ||||||
|         "HEADERS": Object { |  | ||||||
|           "authentication": "hello", |  | ||||||
|         }, |  | ||||||
|         "REQUEST": "GET /posts/1", |         "REQUEST": "GET /posts/1", | ||||||
|       }, |       }, | ||||||
|       Object { |       "plugins": Plugins { | ||||||
|  |         "autoload": Array [ | ||||||
|  |           "std", | ||||||
|  |         ], | ||||||
|  |         "context": Object {}, | ||||||
|  |         "registry": Object { | ||||||
|  |           "dynamicValues": Array [], | ||||||
|  |           "postRequestModifiers": Array [], | ||||||
|  |           "preRequestModifiers": Array [], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     Request { | ||||||
|  |       "ALIAS": "user", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|  |       "DEPENDENCIES": Set {}, | ||||||
|  |       "ENDPOINT": "http://example.com", | ||||||
|  |       "HEADERS": Object { | ||||||
|  |         "hello": "world", | ||||||
|  |       }, | ||||||
|  |       "PATH": "/user", | ||||||
|  |       "REQUEST": "GET /user", | ||||||
|  |       "VERB": "GET", | ||||||
|  |       "originalRequest": Object { | ||||||
|         "ALIAS": "user", |         "ALIAS": "user", | ||||||
|         "ENDPOINT": "http://jsonplaceholder.typicode.com", |         "COOKIEJAR": false, | ||||||
|  |         "ENDPOINT": "http://example.com", | ||||||
|         "HEADERS": Object { |         "HEADERS": Object { | ||||||
|           "authentication": "hello", |  | ||||||
|           "hello": "world", |           "hello": "world", | ||||||
|         }, |         }, | ||||||
|         "REQUEST": "GET /user", |         "REQUEST": "GET /user", | ||||||
|       }, |       }, | ||||||
|     ], |       "plugins": Plugins { | ||||||
|   }, |         "autoload": Array [ | ||||||
|  |           "std", | ||||||
|  |         ], | ||||||
|  |         "context": Object {}, | ||||||
|  |         "registry": Object { | ||||||
|  |           "dynamicValues": Array [], | ||||||
|  |           "postRequestModifiers": Array [], | ||||||
|  |           "preRequestModifiers": Array [], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
| } | } | ||||||
| `; | `; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| 
 | 
 | ||||||
| exports[`Config should load multiple hosts 1`] = ` | exports[`Config should load multiple hosts 1`] = ` | ||||||
| Config { | Config { | ||||||
|   "CACHE": false, |   "COOKIEJAR": false, | ||||||
|   "DEFAULTS": Object { |   "DEFAULTS": Object { | ||||||
|     "HEADERS": Object { |     "HEADERS": Object { | ||||||
|       "hello": "mars", |       "hello": "mars", | ||||||
|  | @ -41,69 +41,21 @@ Config { | ||||||
|       "host": "info", |       "host": "info", | ||||||
|     }, |     }, | ||||||
|   ], |   ], | ||||||
|   "PLUGINS": Array [], |   "PLUGINS": Plugins { | ||||||
|   "VERSION": 1, |     "autoload": Array [ | ||||||
|   "configKeys": Array [ |       "std", | ||||||
|     "VERSION", |  | ||||||
|     "CACHE", |  | ||||||
|     "ENDPOINT", |  | ||||||
|     "PLUGINS", |  | ||||||
|     "DEFAULTS", |  | ||||||
|     "ENVIRONMENT", |  | ||||||
|     "HOSTS", |  | ||||||
|   ], |  | ||||||
|   "defaultConfigValues": Object { |  | ||||||
|     "CACHE": false, |  | ||||||
|     "DEFAULTS": Object {}, |  | ||||||
|     "ENDPOINT": "", |  | ||||||
|     "ENVIRONMENT": Object {}, |  | ||||||
|     "HOSTS": Array [], |  | ||||||
|     "PLUGINS": Array [], |  | ||||||
|     "VERSION": 1, |  | ||||||
|   }, |  | ||||||
|   "doc": Object { |  | ||||||
|     "GET /e1": "e1", |  | ||||||
|     "defaults": Object { |  | ||||||
|       "HEADERS": Object { |  | ||||||
|         "hello": "mars", |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     "endpoint": "http://example.org", |  | ||||||
|     "hosts": Array [ |  | ||||||
|       Object { |  | ||||||
|         "GET /e2": "e2", |  | ||||||
|         "GET /posts": "posts", |  | ||||||
|         "defaults": Object { |  | ||||||
|           "HEADERS": Object { |  | ||||||
|             "hello": "world", |  | ||||||
|             "world": "hello", |  | ||||||
|           }, |  | ||||||
|         }, |  | ||||||
|         "endpoint": "http://example.com", |  | ||||||
|         "host": "com", |  | ||||||
|       }, |  | ||||||
|       Object { |  | ||||||
|         "GET /e3": "e3", |  | ||||||
|         "GET /posts": "posts", |  | ||||||
|         "defaults": Object { |  | ||||||
|           "HEADERS": Object { |  | ||||||
|             "hello": "world", |  | ||||||
|             "world": "bye", |  | ||||||
|           }, |  | ||||||
|         }, |  | ||||||
|         "endpoint": "http://example.net", |  | ||||||
|         "host": "net", |  | ||||||
|       }, |  | ||||||
|       Object { |  | ||||||
|         "GET /posts": "posts", |  | ||||||
|         "endpoint": "http://example.info", |  | ||||||
|         "host": "info", |  | ||||||
|       }, |  | ||||||
|     ], |     ], | ||||||
|  |     "context": Object {}, | ||||||
|  |     "registry": Object { | ||||||
|  |       "dynamicValues": Array [], | ||||||
|  |       "postRequestModifiers": Array [], | ||||||
|  |       "preRequestModifiers": Array [], | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
|   "requests": Array [ |   "REQUESTS": Array [ | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "e1", |       "ALIAS": "e1", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|       "ENDPOINT": "http://example.org", |       "ENDPOINT": "http://example.org", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "hello": "mars", |         "hello": "mars", | ||||||
|  | @ -112,6 +64,7 @@ Config { | ||||||
|     }, |     }, | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "com:e2", |       "ALIAS": "com:e2", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|       "ENDPOINT": "http://example.com", |       "ENDPOINT": "http://example.com", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "hello": "world", |         "hello": "world", | ||||||
|  | @ -121,6 +74,7 @@ Config { | ||||||
|     }, |     }, | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "com:posts", |       "ALIAS": "com:posts", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|       "ENDPOINT": "http://example.com", |       "ENDPOINT": "http://example.com", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "hello": "world", |         "hello": "world", | ||||||
|  | @ -130,6 +84,7 @@ Config { | ||||||
|     }, |     }, | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "net:e3", |       "ALIAS": "net:e3", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|       "ENDPOINT": "http://example.net", |       "ENDPOINT": "http://example.net", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "hello": "world", |         "hello": "world", | ||||||
|  | @ -139,6 +94,7 @@ Config { | ||||||
|     }, |     }, | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "net:posts", |       "ALIAS": "net:posts", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|       "ENDPOINT": "http://example.net", |       "ENDPOINT": "http://example.net", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "hello": "world", |         "hello": "world", | ||||||
|  | @ -148,6 +104,7 @@ Config { | ||||||
|     }, |     }, | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "info:posts", |       "ALIAS": "info:posts", | ||||||
|  |       "COOKIEJAR": false, | ||||||
|       "ENDPOINT": "http://example.info", |       "ENDPOINT": "http://example.info", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "hello": "mars", |         "hello": "mars", | ||||||
|  | @ -155,60 +112,46 @@ Config { | ||||||
|       "REQUEST": "GET /posts", |       "REQUEST": "GET /posts", | ||||||
|     }, |     }, | ||||||
|   ], |   ], | ||||||
| } |  | ||||||
| `; |  | ||||||
| 
 |  | ||||||
| exports[`Config should set up defaults for all requests 1`] = ` |  | ||||||
| Config { |  | ||||||
|   "CACHE": false, |  | ||||||
|   "DEFAULTS": Object { |  | ||||||
|     "HEADERS": Object { |  | ||||||
|       "authentication": "hello", |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   "ENDPOINT": "http://jsonplaceholder.typicode.com", |  | ||||||
|   "ENVIRONMENT": Object {}, |  | ||||||
|   "HOSTS": Array [], |  | ||||||
|   "PLUGINS": Array [], |  | ||||||
|   "VERSION": 1, |   "VERSION": 1, | ||||||
|   "configKeys": Array [ |   "configKeys": Array [ | ||||||
|     "VERSION", |     "VERSION", | ||||||
|     "CACHE", |  | ||||||
|     "ENDPOINT", |     "ENDPOINT", | ||||||
|     "PLUGINS", |     "PLUGINS", | ||||||
|     "DEFAULTS", |     "DEFAULTS", | ||||||
|     "ENVIRONMENT", |     "ENVIRONMENT", | ||||||
|     "HOSTS", |     "HOSTS", | ||||||
|  |     "COOKIEJAR", | ||||||
|   ], |   ], | ||||||
|   "defaultConfigValues": Object { | } | ||||||
|     "CACHE": false, | `; | ||||||
|     "DEFAULTS": Object {}, | 
 | ||||||
|     "ENDPOINT": "", | exports[`Config should set up defaults for all requests 1`] = ` | ||||||
|     "ENVIRONMENT": Object {}, | Config { | ||||||
|     "HOSTS": Array [], |   "COOKIEJAR": false, | ||||||
|     "PLUGINS": Array [], |   "DEFAULTS": Object { | ||||||
|     "VERSION": 1, |     "HEADERS": Object { | ||||||
|   }, |       "authentication": "hello", | ||||||
|   "doc": Object { |  | ||||||
|     "GET /posts/1": "get-post", |  | ||||||
|     "GET /user": Object { |  | ||||||
|       "alias": "user", |  | ||||||
|       "headers": Object { |  | ||||||
|         "hello": "world", |  | ||||||
|       }, |  | ||||||
|     }, |     }, | ||||||
|     "defaults": Object { |  | ||||||
|       "HEADERS": Object { |  | ||||||
|         "authentication": "hello", |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     "endpoint": "http://jsonplaceholder.typicode.com", |  | ||||||
|     "version": 1, |  | ||||||
|   }, |   }, | ||||||
|   "requests": Array [ |   "ENDPOINT": "http://example.com", | ||||||
|  |   "ENVIRONMENT": Object {}, | ||||||
|  |   "HOSTS": Array [], | ||||||
|  |   "PLUGINS": Plugins { | ||||||
|  |     "autoload": Array [ | ||||||
|  |       "std", | ||||||
|  |     ], | ||||||
|  |     "context": Object {}, | ||||||
|  |     "registry": Object { | ||||||
|  |       "dynamicValues": Array [], | ||||||
|  |       "postRequestModifiers": Array [], | ||||||
|  |       "preRequestModifiers": Array [], | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   "REQUESTS": Array [ | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "get-post", |       "ALIAS": "get-post", | ||||||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", |       "COOKIEJAR": false, | ||||||
|  |       "ENDPOINT": "http://example.com", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "authentication": "hello", |         "authentication": "hello", | ||||||
|       }, |       }, | ||||||
|  | @ -216,7 +159,8 @@ Config { | ||||||
|     }, |     }, | ||||||
|     Object { |     Object { | ||||||
|       "ALIAS": "user", |       "ALIAS": "user", | ||||||
|       "ENDPOINT": "http://jsonplaceholder.typicode.com", |       "COOKIEJAR": false, | ||||||
|  |       "ENDPOINT": "http://example.com", | ||||||
|       "HEADERS": Object { |       "HEADERS": Object { | ||||||
|         "authentication": "hello", |         "authentication": "hello", | ||||||
|         "hello": "world", |         "hello": "world", | ||||||
|  | @ -224,5 +168,15 @@ Config { | ||||||
|       "REQUEST": "GET /user", |       "REQUEST": "GET /user", | ||||||
|     }, |     }, | ||||||
|   ], |   ], | ||||||
|  |   "VERSION": 1, | ||||||
|  |   "configKeys": Array [ | ||||||
|  |     "VERSION", | ||||||
|  |     "ENDPOINT", | ||||||
|  |     "PLUGINS", | ||||||
|  |     "DEFAULTS", | ||||||
|  |     "ENVIRONMENT", | ||||||
|  |     "HOSTS", | ||||||
|  |     "COOKIEJAR", | ||||||
|  |   ], | ||||||
| } | } | ||||||
| `; | `; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||||
|  | 
 | ||||||
|  | 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 { | ||||||
|  |   "autoload": Array [], | ||||||
|  |   "context": Object {}, | ||||||
|  |   "registry": Object { | ||||||
|  |     "dynamicValues": Array [], | ||||||
|  |     "postRequestModifiers": Array [], | ||||||
|  |     "preRequestModifiers": Array [], | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | `; | ||||||
|  | @ -7,7 +7,7 @@ Object { | ||||||
|     "body": Object { |     "body": Object { | ||||||
|       "username": "seich", |       "username": "seich", | ||||||
|     }, |     }, | ||||||
|     "endpoint": "http://martianwabbit.com/user", |     "endpoint": "http://example.com/user", | ||||||
|     "headers": Object { |     "headers": Object { | ||||||
|       "authentication": "BEARER abc123", |       "authentication": "BEARER abc123", | ||||||
|     }, |     }, | ||||||
|  | @ -24,9 +24,9 @@ exports[`Request should execute a request 2`] = ` | ||||||
| Object { | Object { | ||||||
|   "body": "{\\"hello\\": \\"world\\"}", |   "body": "{\\"hello\\": \\"world\\"}", | ||||||
|   "request": Object { |   "request": Object { | ||||||
|     "body": Object {}, |     "body": undefined, | ||||||
|     "endpoint": "http://martianwabbit.com/user", |     "endpoint": "http://example.com/user", | ||||||
|     "headers": Object {}, |     "headers": undefined, | ||||||
|   }, |   }, | ||||||
|   "response": Object { |   "response": Object { | ||||||
|     "body": "{\\"hello\\": \\"world\\"}", |     "body": "{\\"hello\\": \\"world\\"}", | ||||||
|  | @ -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,14 +3,13 @@ | ||||||
| exports[`RequestList should execute requests by alias. 1`] = ` | exports[`RequestList should execute requests by alias. 1`] = ` | ||||||
| Object { | Object { | ||||||
|   "body": "{\\"hello\\": \\"world\\"}", |   "body": "{\\"hello\\": \\"world\\"}", | ||||||
|   "changed": true, |  | ||||||
|   "request": Object { |   "request": Object { | ||||||
|     "body": Object { |     "body": Object { | ||||||
|       "lastname": "Diaz", |       "lastname": "Diaz", | ||||||
|       "name": "Sergio", |       "name": "Sergio", | ||||||
|     }, |     }, | ||||||
|     "endpoint": "http://martianwabbit.com/user", |     "endpoint": "http://martianwabbit.com/user", | ||||||
|     "headers": Object {}, |     "headers": undefined, | ||||||
|   }, |   }, | ||||||
|   "response": Object { |   "response": Object { | ||||||
|     "body": "{\\"hello\\": \\"world\\"}", |     "body": "{\\"hello\\": \\"world\\"}", | ||||||
|  | @ -19,3 +18,26 @@ Object { | ||||||
|   }, |   }, | ||||||
| } | } | ||||||
| `; | `; | ||||||
|  | 
 | ||||||
|  | exports[`RequestList should fetch dependencies 1`] = ` | ||||||
|  | RequestCache { | ||||||
|  |   "$cache": Object { | ||||||
|  |     "env": Object { | ||||||
|  |       "environmental": true, | ||||||
|  |     }, | ||||||
|  |     "get-posts": Object { | ||||||
|  |       "body": "{\\"hello\\": \\"world\\"}", | ||||||
|  |       "request": Object { | ||||||
|  |         "body": undefined, | ||||||
|  |         "endpoint": "http://martianwabbit.com/post", | ||||||
|  |         "headers": undefined, | ||||||
|  |       }, | ||||||
|  |       "response": Object { | ||||||
|  |         "body": "{\\"hello\\": \\"world\\"}", | ||||||
|  |         "headers": Array [], | ||||||
|  |         "status": 200, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | `; | ||||||
|  |  | ||||||
|  | @ -1,26 +1,59 @@ | ||||||
| const yaml = require('js-yaml'); | const yaml = require('js-yaml') | ||||||
| const Beau = require('../beau'); | const Beau = require('../beau') | ||||||
|  | const { moduleVersion } = require('../shared') | ||||||
|  | 
 | ||||||
|  | jest.mock('../shared') | ||||||
|  | 
 | ||||||
|  | const requireg = require('requireg') | ||||||
|  | requireg.resolving = false | ||||||
| 
 | 
 | ||||||
| describe(`Beau's config Loader.`, () => { | describe(`Beau's config Loader.`, () => { | ||||||
|     it('should create a request list', () => { |     it('should load the config', () => { | ||||||
|         const doc = yaml.safeLoad(` |         moduleVersion.mockReturnValue(1) | ||||||
|  | 
 | ||||||
|  |         const doc = yaml.load(` | ||||||
|             version: 1 |             version: 1 | ||||||
|             endpoint: 'http://jsonplaceholder.typicode.com' |             endpoint: 'http://example.com' | ||||||
| 
 | 
 | ||||||
|             defaults: |             defaults: | ||||||
|                 headers: |                 headers: | ||||||
|                     authentication: hello |                     authentication: hello | ||||||
|  |         `)
 | ||||||
|  | 
 | ||||||
|  |         const beau = new Beau(doc) | ||||||
|  |         expect(beau).toMatchSnapshot() | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     it(`should load the request list using the configuration`, () => { | ||||||
|  |         moduleVersion.mockReturnValue(1) | ||||||
|  | 
 | ||||||
|  |         const doc = yaml.load(` | ||||||
|  |             version: 1 | ||||||
|  |             endpoint: 'http://example.com' | ||||||
| 
 | 
 | ||||||
|             GET /posts/1: get-post |             GET /posts/1: get-post | ||||||
|             GET /user: |             GET /user: | ||||||
|                 alias: user |                 alias: user | ||||||
|                 headers: |                 headers: | ||||||
|                     hello: world |                     hello: world | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|         const beau = new Beau(doc); |         const beau = new Beau(doc) | ||||||
|  |         expect(beau.requests).toMatchSnapshot() | ||||||
|  |     }) | ||||||
| 
 | 
 | ||||||
|         expect(beau.requests).toBeDefined(); |     it('should display a warning if the module version and the beau file version are different', () => { | ||||||
|         expect(beau).toMatchSnapshot(); |         let stdout | ||||||
|     }); |         let spy = jest | ||||||
| }); |             .spyOn(console, 'warn') | ||||||
|  |             .mockImplementation((val) => (stdout = val)) | ||||||
|  | 
 | ||||||
|  |         moduleVersion.mockReturnValue(2) | ||||||
|  | 
 | ||||||
|  |         const beau = new Beau({ version: 1 }) | ||||||
|  |         expect(stdout).toEqual('This Beau file expects v1. You are using v2.') | ||||||
|  | 
 | ||||||
|  |         spy.mockReset() | ||||||
|  |         spy.mockRestore() | ||||||
|  |     }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | @ -1,24 +1,25 @@ | ||||||
| const yaml = require('js-yaml'); | const yaml = require('js-yaml') | ||||||
| const Config = require('../config'); | const Config = require('../config') | ||||||
|  | 
 | ||||||
|  | const requireg = require('requireg') | ||||||
|  | requireg.resolving = false | ||||||
| 
 | 
 | ||||||
| describe('Config', () => { | describe('Config', () => { | ||||||
|   it('should load valid config keys', () => { |     it('should load valid config keys', () => { | ||||||
|     const doc = yaml.safeLoad(` |         const doc = yaml.load(` | ||||||
|             version: 1 |             version: 1 | ||||||
|             endpoint: http://martianwabbit.com
 |             endpoint: http://martianwabbit.com
 | ||||||
|             cache: false |  | ||||||
|             shouldntBeAdded: true |             shouldntBeAdded: true | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|     const config = new Config(doc); |         const config = new Config(doc) | ||||||
|     expect(config.ENDPOINT).toBe(doc.endpoint); |         expect(config.ENDPOINT).toBe(doc.endpoint) | ||||||
|     expect(config.CACHE).toBe(doc.cache); |         expect(config.VERSION).toBe(doc.version) | ||||||
|     expect(config.VERSION).toBe(doc.version); |         expect(config.shouldntBeAdded).toBeUndefined() | ||||||
|     expect(config.shouldntBeAdded).toBeUndefined(); |     }) | ||||||
|   }); |  | ||||||
| 
 | 
 | ||||||
|   it('should load requests', () => { |     it('should load requests', () => { | ||||||
|     const doc = yaml.safeLoad(` |         const doc = yaml.load(` | ||||||
|             endpoint: http://example.com
 |             endpoint: http://example.com
 | ||||||
| 
 | 
 | ||||||
|             GET /profile: get-profile |             GET /profile: get-profile | ||||||
|  | @ -28,16 +29,16 @@ describe('Config', () => { | ||||||
|                 alias: user |                 alias: user | ||||||
|                 headers: |                 headers: | ||||||
|                     hello: world |                     hello: world | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|     const config = new Config(doc); |         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', () => { |     it('should set up defaults for all requests', () => { | ||||||
|     const doc = yaml.safeLoad(` |         const doc = yaml.load(` | ||||||
|             version: 1 |             version: 1 | ||||||
|             endpoint: 'http://jsonplaceholder.typicode.com' |             endpoint: 'http://example.com' | ||||||
| 
 | 
 | ||||||
|             defaults: |             defaults: | ||||||
|                 HEADERS: |                 HEADERS: | ||||||
|  | @ -48,18 +49,19 @@ describe('Config', () => { | ||||||
|                 alias: user |                 alias: user | ||||||
|                 headers: |                 headers: | ||||||
|                     hello: world |                     hello: world | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|     const config = new Config(doc); |         const config = new Config(doc) | ||||||
| 
 | 
 | ||||||
|     expect(config).toMatchSnapshot(); |         expect(config).toMatchSnapshot() | ||||||
|     Object.values(config.requests).forEach(r => { |         Object.values(config.REQUESTS).forEach((r) => { | ||||||
|       expect(r.HEADERS.authentication).toMatch('hello'); |             expect(r.HEADERS.authentication).toMatch('hello') | ||||||
|     }); |         }) | ||||||
|   }); |     }) | ||||||
| 
 | 
 | ||||||
|   it('should load multiple hosts', () => { |     it('should load multiple hosts', () => { | ||||||
|     const doc = yaml.safeLoad(` |         const doc = yaml.load(` | ||||||
|  |           version: 1 | ||||||
|           endpoint: http://example.org
 |           endpoint: http://example.org
 | ||||||
| 
 | 
 | ||||||
|           defaults: |           defaults: | ||||||
|  | @ -95,15 +97,15 @@ describe('Config', () => { | ||||||
|               endpoint: http://example.info
 |               endpoint: http://example.info
 | ||||||
| 
 | 
 | ||||||
|               GET /posts: posts |               GET /posts: posts | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|     let config = new Config(doc); |         let config = new Config(doc) | ||||||
| 
 | 
 | ||||||
|     expect(config).toMatchSnapshot(); |         expect(config).toMatchSnapshot() | ||||||
|   }); |     }) | ||||||
| 
 | 
 | ||||||
|   it('should namespace all aliases within an host', () => { |     it('should namespace all aliases within an host', () => { | ||||||
|     const doc = yaml.safeLoad(` |         const doc = yaml.load(` | ||||||
|             hosts: |             hosts: | ||||||
|               - host: test1 |               - host: test1 | ||||||
|                 endpoint: http://example.com
 |                 endpoint: http://example.com
 | ||||||
|  | @ -111,16 +113,16 @@ describe('Config', () => { | ||||||
|               - host: test2 |               - host: test2 | ||||||
|                 endpoint: http://example.net
 |                 endpoint: http://example.net
 | ||||||
|                 GET /posts: posts |                 GET /posts: posts | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|     let config = new Config(doc); |         let config = new Config(doc) | ||||||
| 
 | 
 | ||||||
|     expect(config.requests[0].ALIAS).toBe('test1:posts'); |         expect(config.REQUESTS[0].ALIAS).toBe('test1:posts') | ||||||
|     expect(config.requests[1].ALIAS).toBe('test2:posts'); |         expect(config.REQUESTS[1].ALIAS).toBe('test2:posts') | ||||||
|   }); |     }) | ||||||
| 
 | 
 | ||||||
|   it(`should throw if host doesn't have a host key`, () => { |     it(`should throw if host doesn't have a host key`, () => { | ||||||
|     const doc = yaml.safeLoad(` |         const doc = yaml.load(` | ||||||
|             hosts: |             hosts: | ||||||
|               - endpoint: http://example.com
 |               - endpoint: http://example.com
 | ||||||
|                 GET /posts: posts |                 GET /posts: posts | ||||||
|  | @ -128,13 +130,13 @@ describe('Config', () => { | ||||||
|               - host: test2 |               - host: test2 | ||||||
|                 endpoint: http://example.net
 |                 endpoint: http://example.net
 | ||||||
|                 GET /posts: posts |                 GET /posts: posts | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|     expect(() => new Config(doc)).toThrow(); |         expect(() => new Config(doc)).toThrow() | ||||||
|   }); |     }) | ||||||
| 
 | 
 | ||||||
|   it(`should merge host settings with global settings`, () => { |     it(`should merge host settings with global settings`, () => { | ||||||
|     const doc = yaml.safeLoad(` |         const doc = yaml.load(` | ||||||
|             defaults: |             defaults: | ||||||
|               headers: |               headers: | ||||||
|                 hello: 1 |                 hello: 1 | ||||||
|  | @ -150,9 +152,25 @@ describe('Config', () => { | ||||||
|                   headers: false |                   headers: false | ||||||
| 
 | 
 | ||||||
|                 GET /posts: posts |                 GET /posts: posts | ||||||
|         `);
 |         `)
 | ||||||
| 
 | 
 | ||||||
|     let config = new Config(doc); |         let config = new Config(doc) | ||||||
|     expect(config.requests[0].HEADERS.hello).toBe(1); |         expect(config.REQUESTS[0].HEADERS.hello).toBe(1) | ||||||
|   }); |     }) | ||||||
| }); | 
 | ||||||
|  |     it(`should allow different settings for the same request`, () => { | ||||||
|  |         const doc = yaml.load(` | ||||||
|  |       host: https://example.com
 | ||||||
|  |       GET /1: | ||||||
|  |         - alias: req1 | ||||||
|  |           headers: | ||||||
|  |             request: 1 | ||||||
|  |         - alias: req2 | ||||||
|  |           headers: | ||||||
|  |             request: 2 | ||||||
|  |     `)
 | ||||||
|  | 
 | ||||||
|  |         let config = new Config(doc) | ||||||
|  |         expect(config.REQUESTS.length).toBe(2) | ||||||
|  |     }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,101 @@ | ||||||
|  | const Plugins = require('../plugins') | ||||||
|  | const Request = require('../request') | ||||||
|  | const RequestCache = require('../requestCache') | ||||||
|  | const requireg = require('requireg') | ||||||
|  | 
 | ||||||
|  | describe(`Beau's plugin system`, () => { | ||||||
|  |     let request | ||||||
|  |     let plugins | ||||||
|  | 
 | ||||||
|  |     beforeEach(() => { | ||||||
|  |         plugins = new Plugins([{ Modifiers: [Object] }, 'DynamicValues'], []) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     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() | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     it(`should warn if the plugin is not available.`, () => { | ||||||
|  |         const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}) | ||||||
|  |         requireg.resolving = false | ||||||
|  | 
 | ||||||
|  |         new Plugins(['not-a-Package']) | ||||||
|  |         expect(spy).toHaveBeenCalled() | ||||||
|  | 
 | ||||||
|  |         requireg.resolving = true | ||||||
|  |         spy.mockReset() | ||||||
|  |         spy.mockRestore() | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     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)]', | ||||||
|  |                         empty: '' | ||||||
|  |                     }, | ||||||
|  |                     payload: 'counted $[add(1, $value2)] so far.' | ||||||
|  |                 }, | ||||||
|  |                 plugins | ||||||
|  |             ) | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         let cache = new RequestCache() | ||||||
|  |         cache.add('value2', '2') | ||||||
|  | 
 | ||||||
|  |         it(`should look for dynamic values executing and replacing them`, async () => { | ||||||
|  |             let req = await request.exec(cache) | ||||||
|  |             expect(req).toHaveProperty('request.body', 'counted 3 so far.') | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         it(`should change the internal datatype if the only thing in the value is the dynamic value`, async () => { | ||||||
|  |             let req = await request.exec(cache) | ||||||
|  |             expect(req).toHaveProperty('request.headers.count', 3) | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         it(`should return empty values as empty`, async () => { | ||||||
|  |             let req = await request.exec(cache) | ||||||
|  |             expect(req).toHaveProperty('request.headers.empty', '') | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         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() | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | }) | ||||||
|  | @ -1,94 +1,100 @@ | ||||||
| const Request = require('../request'); | const Request = require('../request') | ||||||
| const RequestCache = require('../requestCache'); | const RequestCache = require('../requestCache') | ||||||
| const RequestList = require('../requestList'); | const requestPromiseNativeMock = require('request-promise-native') | ||||||
| const requestPromiseNativeMock = require('request-promise-native'); |  | ||||||
| 
 | 
 | ||||||
| describe('Request', () => { | describe('Request', () => { | ||||||
| 	let cache; |     let cache | ||||||
| 	let validRequestConfig; |     let validRequestConfig | ||||||
| 	let invalidRequestConfig; |     let invalidRequestConfig | ||||||
| 	let request; |     let request | ||||||
| 	let requestWithoutDependencies; |     let requestWithoutDependencies | ||||||
| 
 | 
 | ||||||
| 	beforeEach(() => { |     beforeEach(() => { | ||||||
| 		validRequestConfig = { |         validRequestConfig = { | ||||||
| 			request: 'POST /user', |             request: 'POST /user', | ||||||
| 			endpoint: 'http://martianwabbit.com', |             endpoint: 'http://example.com', | ||||||
| 			alias: 'update', |             alias: 'update', | ||||||
| 			params: { |             params: { | ||||||
| 				userId: '$profile.UserId' |                 userId: '$profile.UserId' | ||||||
| 			}, |             }, | ||||||
| 			headers: { |             headers: { | ||||||
| 				authentication: 'BEARER $session.token' |                 authentication: 'BEARER $session.token' | ||||||
| 			}, |             }, | ||||||
| 			payload: { |             payload: { | ||||||
| 				username: 'seich' |                 username: 'seich' | ||||||
| 			} |             } | ||||||
| 		}; |         } | ||||||
| 
 | 
 | ||||||
| 		invalidRequestConfig = { |         invalidRequestConfig = { | ||||||
| 			request: `POST /session`, |             request: `POST /session`, | ||||||
| 			endpoint: 'http://martianwabbit.com' |             endpoint: 'http://example.com' | ||||||
| 		}; |         } | ||||||
| 
 | 
 | ||||||
| 		cache = new RequestCache(); |         cache = new RequestCache() | ||||||
| 		cache.add('$session', { token: 'abc123' }); |         cache.add('session', { token: 'abc123' }) | ||||||
| 		cache.add('$profile', { UserId: 14 }); |         cache.add('profile', { UserId: 14 }) | ||||||
| 
 | 
 | ||||||
| 		request = new Request(validRequestConfig); |         request = new Request(validRequestConfig) | ||||||
| 		requestWithoutDependencies = new Request({ |         requestWithoutDependencies = new Request({ | ||||||
| 			endpoint: 'http://martianwabbit.com', |             endpoint: 'http://example.com', | ||||||
| 			request: 'GET /user', |             request: 'GET /user', | ||||||
| 			alias: 'show' |             alias: 'show' | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		requestPromiseNativeMock.fail = false; |         requestPromiseNativeMock.fail = false | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should load up the given request', () => { |     it('should load up the given request', () => { | ||||||
| 		expect(request.VERB).toBe('POST'); |         expect(request.VERB).toBe('POST') | ||||||
| 		expect(request.ENDPOINT).toBe(validRequestConfig.endpoint + '/user'); |         expect(request.ENDPOINT).toBe(validRequestConfig.endpoint) | ||||||
| 		expect(request.HEADERS).toBeDefined(); |         expect(request.HEADERS).toBeDefined() | ||||||
| 		expect(request.PAYLOAD).toBeDefined(); |         expect(request.PAYLOAD).toBeDefined() | ||||||
| 		expect(request.PARAMS).toBeDefined(); |         expect(request.PARAMS).toBeDefined() | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should throw if a given request is invalid', () => { |     it('should throw if a given request is invalid', () => { | ||||||
| 		expect(() => new Request(invalidRequestConfig)).toThrow(); |         expect(() => new Request(invalidRequestConfig)).toThrow() | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should list all of its dependencies', () => { |     it('should list all of its dependencies', () => { | ||||||
| 		expect(request.DEPENDENCIES.size).toBe(2); |         expect(request.DEPENDENCIES.size).toBe(2) | ||||||
| 		expect(request.DEPENDENCIES).toContain('session'); |         expect(request.DEPENDENCIES).toContain('session') | ||||||
| 		expect(request.DEPENDENCIES).toContain('profile'); |         expect(request.DEPENDENCIES).toContain('profile') | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should execute a request', async () => { |     it('should execute a request', async () => { | ||||||
| 		await expect(request.exec([], cache)).resolves.toMatchSnapshot(); |         await expect(request.exec(cache)).resolves.toMatchSnapshot() | ||||||
| 		await expect( |         await expect( | ||||||
| 			requestWithoutDependencies.exec() |             requestWithoutDependencies.exec() | ||||||
| 		).resolves.toMatchSnapshot(); |         ).resolves.toMatchSnapshot() | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should throw if the request fails', async () => { |     it('should throw if the request fails', async () => { | ||||||
| 		requestPromiseNativeMock.fail = true; |         requestPromiseNativeMock.fail = true | ||||||
| 		await expect(requestWithoutDependencies.exec()).rejects.toThrow(Error); |         await expect(requestWithoutDependencies.exec()).rejects.toThrow(Error) | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should use modifiers', async () => { |     it(`should use the full url if given one as part of the path instead of the global endpoint`, async () => { | ||||||
| 		const preRequest = jest.fn(); |         const requestWithPath = new Request({ | ||||||
| 		const withPreRequest = [{ preRequest }]; |             endpoint: 'http://example.com', | ||||||
|  |             request: 'GET http://martianwabbit.com/user', | ||||||
|  |             alias: 'get-user' | ||||||
|  |         }) | ||||||
| 
 | 
 | ||||||
| 		const notCalled = jest.fn(); |         const requestWithoutPath = new Request({ | ||||||
| 		const nonModifiers = [{ notCalled }]; |             endpoint: 'http://example.com', | ||||||
|  |             request: 'GET /user', | ||||||
|  |             alias: 'get-user' | ||||||
|  |         }) | ||||||
| 
 | 
 | ||||||
| 		await requestWithoutDependencies.exec(withPreRequest); |         await expect(requestWithPath.exec()).resolves.toHaveProperty( | ||||||
|  |             'request.endpoint', | ||||||
|  |             'http://martianwabbit.com/user' | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
| 		expect(preRequest).toHaveBeenCalled(); |         await expect(requestWithoutPath.exec()).resolves.toHaveProperty( | ||||||
| 		expect(preRequest.mock.calls).toMatchSnapshot(); |             'request.endpoint', | ||||||
| 
 |             'http://example.com/user' | ||||||
| 		await requestWithoutDependencies.exec(nonModifiers); |         ) | ||||||
| 
 |     }) | ||||||
| 		expect(notCalled).not.toHaveBeenCalled(); | }) | ||||||
| 	}); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
|  | @ -1,83 +1,89 @@ | ||||||
| const RequestCache = require('../requestCache'); | const RequestCache = require('../requestCache') | ||||||
| 
 | 
 | ||||||
| describe('Request Cache', () => { | describe('Request Cache', () => { | ||||||
| 	let cache; |     let cache | ||||||
| 
 | 
 | ||||||
| 	beforeEach(() => { |     beforeEach(() => { | ||||||
| 		cache = new RequestCache(); |         cache = new RequestCache() | ||||||
| 
 | 
 | ||||||
| 		cache.add('$session', { |         cache.add('session', { | ||||||
| 			hello: 'World' |             hello: 'World' | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		cache.add('$array', [ |         cache.add('array', [ | ||||||
| 			{ |             { | ||||||
| 				id: 1, |                 id: 1, | ||||||
| 				name: 'Sergio' |                 name: 'Sergio' | ||||||
| 			}, |             }, | ||||||
| 			{ |             { | ||||||
| 				id: 2, |                 id: 2, | ||||||
| 				name: 'Angela' |                 name: 'Angela' | ||||||
| 			} |             } | ||||||
| 		]); |         ]) | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should add keys to the cache', () => { |     it('should add keys to the cache', () => { | ||||||
| 		expect(cache.$cache.$session.hello).toBe('World'); |         expect(cache.$cache.session.hello).toBe('World') | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	describe('get', () => { |     describe('get', () => { | ||||||
| 		it('should be able to find key values with a given path', () => { |         it('should be able to find key values with a given path', () => { | ||||||
| 			expect(cache.get('$session.hello')).toBe('World'); |             expect(cache.get('session.hello')).toBe('World') | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		it('should throw when given an invalid path', () => { |         it('should throw when given an invalid path', () => { | ||||||
| 			expect(() => cache.get('$session.world')).toThrow(); |             expect(() => cache.get('$session.world')).toThrow() | ||||||
| 		}); |         }) | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	describe('parse', () => { |     describe('parse', () => { | ||||||
| 		it("should transform variables in strings using it's cache", () => { |         it("should transform variables in strings using it's cache", () => { | ||||||
| 			expect(cache.parse('Hello $session.hello')).toBe('Hello World'); |             expect(cache.parse('Hello $session.hello')).toBe('Hello World') | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		it('should go transform variables in all values when given an object', () => { |         it('should go transform variables in all values when given an object', () => { | ||||||
| 			let parsed = cache.parse({ |             let parsed = cache.parse({ | ||||||
| 				hello: 'hello $session.hello', |                 hello: 'hello $session.hello', | ||||||
| 				earth: '$session.hello' |                 earth: '$session.hello' | ||||||
| 			}); |             }) | ||||||
| 			expect(parsed.hello).toBe('hello World'); |             expect(parsed.hello).toBe('hello World') | ||||||
| 			expect(parsed.earth).toBe('World'); |             expect(parsed.earth).toBe('World') | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		it('should return every non-string value as-is', () => { |         it('should return every non-string value as-is', () => { | ||||||
| 			let parsed = cache.parse({ |             let parsed = cache.parse({ | ||||||
| 				number: 1, |                 number: 1, | ||||||
| 				nulled: null, |                 nulled: null, | ||||||
| 				truthy: false, |                 truthy: false, | ||||||
| 				hello: '$session.hello' |                 hello: '$session.hello' | ||||||
| 			}); |             }) | ||||||
| 			expect(parsed.number).toBe(1); |             expect(parsed.number).toBe(1) | ||||||
| 			expect(parsed.nulled).toBeNull(); |             expect(parsed.nulled).toBeNull() | ||||||
| 			expect(parsed.truthy).toBe(false); |             expect(parsed.truthy).toBe(false) | ||||||
| 			expect(parsed.hello).toBe('World'); |             expect(parsed.hello).toBe('World') | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		it('should parse arrays as well', () => { |         it('should parse arrays as well', () => { | ||||||
| 			let parsed = cache.parse({ hello: '$array.0.name' }); |             let parsed = cache.parse({ hello: '$array.0.name' }) | ||||||
| 			expect(parsed.hello).toBe('Sergio'); |             expect(parsed.hello).toBe('Sergio') | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		it('should return an object when given an undefined value', () => { |         it('should return an object when given an undefined value', () => { | ||||||
| 			expect(Object.keys(cache.parse(undefined)).length).toBe(0); |             expect(cache.parse(undefined)).toEqual({}) | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		it('should parse any value other than undefined', () => { |         it('should parse any value other than undefined', () => { | ||||||
| 			expect(cache.parse('Hello $session.hello')).toBe('Hello World'); |             expect(cache.parse('Hello $session.hello')).toBe('Hello World') | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		it('should return null when passed null', () => { |         it('should return null when passed null', () => { | ||||||
| 			expect(cache.parse(null)).toBe(null); |             expect(cache.parse(null)).toBe(null) | ||||||
| 		}); |         }) | ||||||
| 	}); | 
 | ||||||
| }); |         it(`shouldn't replace escaped variables`, () => { | ||||||
|  |             expect(cache.parse(`\\$session.hello is $session.hello`)).toBe( | ||||||
|  |                 `$session.hello is World` | ||||||
|  |             ) | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | @ -1,88 +1,78 @@ | ||||||
| const Config = require('../config'); | const Config = require('../config') | ||||||
| const RequestList = require('../requestList'); | const RequestList = require('../requestList') | ||||||
| const requestPromiseNativeMock = require('request-promise-native'); | const requestPromiseNativeMock = require('request-promise-native') | ||||||
| 
 | 
 | ||||||
| describe('RequestList', () => { | describe('RequestList', () => { | ||||||
| 	const endpoint = 'http://martianwabbit.com'; |     const endpoint = 'http://martianwabbit.com' | ||||||
| 
 | 
 | ||||||
| 	let env = { |     let env = { | ||||||
| 		environmental: true |         environmental: true | ||||||
| 	}; |     } | ||||||
| 
 | 
 | ||||||
| 	const doc = { |     const doc = { | ||||||
| 		ENDPOINT: endpoint, |         ENDPOINT: endpoint, | ||||||
| 		ENVIRONMENT: env, |         ENVIRONMENT: env, | ||||||
| 		PLUGINS: [ |         'GET /post': { alias: 'get-posts' }, | ||||||
| 			{ |         'POST /user': { | ||||||
| 				'beau-jwt': { |             alias: 'user', | ||||||
| 					data: { |             payload: { | ||||||
| 						secret: 'shhh.', |                 name: 'Sergio', | ||||||
| 						userId: 412 |                 lastname: 'Diaz' | ||||||
| 					} |             } | ||||||
| 				} |         } | ||||||
| 			}, |     } | ||||||
| 			'beau-document' |  | ||||||
| 		], |  | ||||||
| 		'GET /post': { alias: 'get-posts' }, |  | ||||||
| 		'POST /user': { |  | ||||||
| 			alias: 'user', |  | ||||||
| 			payload: { |  | ||||||
| 				name: 'Sergio', |  | ||||||
| 				lastname: 'Diaz' |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| 
 | 
 | ||||||
| 	let requests; |     let requests | ||||||
| 	beforeEach(() => { |     beforeEach(() => { | ||||||
| 		requestPromiseNativeMock.fail = false; |         requestPromiseNativeMock.fail = false | ||||||
| 
 | 
 | ||||||
| 		let config = new Config(doc); |         let config = new Config(doc) | ||||||
| 		requests = new RequestList(config.requests, config); |         requests = new RequestList(config) | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should load valid requests', () => { |     it('should allow an empty request list', () => { | ||||||
| 		expect(requests.list.length).toBe(2); |         requests = new RequestList() | ||||||
| 	}); |         expect(requests.list.length).toBe(0) | ||||||
|  |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should fetch dependencies', () => { |     it('should load valid requests', () => { | ||||||
| 		requests.fetchDependencies(['get-posts']); |         expect(requests.list.length).toBe(2) | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should load plugins', () => { |     it('should fetch dependencies', async () => { | ||||||
| 		const pluginLessList = new RequestList(); |         await expect( | ||||||
| 		expect(requests.modifiers.length).toBe(2); |             requests.fetchDependencies(['get-posts']) | ||||||
| 		expect(pluginLessList.modifiers.length).toBe(0); |         ).resolves.toMatchSnapshot() | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should execute requests by alias.', async () => { |     it('should execute requests by alias.', async () => { | ||||||
| 		await expect(requests.execByAlias('user')).resolves.toMatchSnapshot(); |         await expect(requests.execByAlias('user')).resolves.toMatchSnapshot() | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should fail if the request fails', async () => { |     it('should fail if the request fails', async () => { | ||||||
| 		requestPromiseNativeMock.fail = true; |         requestPromiseNativeMock.fail = true | ||||||
| 		await expect(requests.execByAlias('user')).rejects.toThrow(); |         await expect(requests.execByAlias('user')).rejects.toThrow() | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should return a cached result if available', async () => { |     it('should return a cached result if available', async () => { | ||||||
| 		const obj = { test: true }; |         const obj = { test: true } | ||||||
| 		requests.cache.add('$test', obj); |         requests.cache.add('test', obj) | ||||||
| 		await expect(requests.execByAlias('test')).resolves.toBe(obj); |         await expect(requests.execByAlias('test')).resolves.toBe(obj) | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it('should fail if the alias is not found', async () => { |     it('should fail if the alias is not found', async () => { | ||||||
| 		await expect(requests.execByAlias('notAnAlias')).rejects.toThrow(); |         await expect(requests.execByAlias('notAnAlias')).rejects.toThrow() | ||||||
| 	}); |     }) | ||||||
| 
 | 
 | ||||||
| 	it(`should fail if a given request doesn't have an alias`, () => { |     it(`should fail if a given request doesn't have an alias`, () => { | ||||||
| 		let config = new Config({ |         let config = new Config({ | ||||||
| 			'GET /hello': { |             'GET /hello': { | ||||||
| 				headers: { |                 headers: { | ||||||
| 					hello: 1 |                     hello: 1 | ||||||
| 				} |                 } | ||||||
| 			} |             } | ||||||
| 		}); |         }) | ||||||
| 
 | 
 | ||||||
| 		expect(() => new RequestList(config.requests, config)).toThrow(); |         expect(() => new RequestList(config, config)).toThrow() | ||||||
| 	}); |     }) | ||||||
| }); | }) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,98 @@ | ||||||
|  | const { | ||||||
|  |     requestRegex, | ||||||
|  |     replacementRegex, | ||||||
|  |     dynamicValueRegex, | ||||||
|  |     UpperCaseKeys, | ||||||
|  |     removeOptionalKeys, | ||||||
|  |     toKebabCase, | ||||||
|  |     replaceInObject, | ||||||
|  |     expandPath | ||||||
|  | } = require('../shared') | ||||||
|  | 
 | ||||||
|  | describe('Shared Utilities', () => { | ||||||
|  |     describe('requestRegex', () => { | ||||||
|  |         test.each([ | ||||||
|  |             ['GET /hello', true], | ||||||
|  |             ['HEAD /hello', true], | ||||||
|  |             ['POST /hello', true], | ||||||
|  |             ['PUT /hello', true], | ||||||
|  |             ['DELETE /hello', true], | ||||||
|  |             ['CONNECT /hello', true], | ||||||
|  |             ['OPTIONS /hello', true], | ||||||
|  |             ['TRACE /hello', true], | ||||||
|  |             ['PATCH /hello', true] | ||||||
|  |         ])('should match: %s', (example, expected) => { | ||||||
|  |             expect(requestRegex.test(example)).toBe(expected) | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     describe('replacementRegex', () => { | ||||||
|  |         test.each([ | ||||||
|  |             ['$a.b', ['$a.b']], | ||||||
|  |             ['GET /hello/$a.name', ['$a.name']], | ||||||
|  |             ['PUT /hi/$a.a/$a.b', ['$a.a', '$a.b']], | ||||||
|  |             [`\\$value`, ['\\$value']] | ||||||
|  |         ])('should match: %s', (example, expected) => { | ||||||
|  |             expect(example.match(replacementRegex)).toEqual(expected) | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     describe('dynamicValueRegex', () => { | ||||||
|  |         test.each([ | ||||||
|  |             ['$[test()]', ['$[test()]']], | ||||||
|  |             ['$[test(1, 2, 3)]', ['$[test(1, 2, 3)]']], | ||||||
|  |             [`$[test({ \n id: 1 \n })]`, ['$[test({ \n id: 1 \n })]']] | ||||||
|  |         ])('should match: %s', (example, expected) => { | ||||||
|  |             expect(example.match(dynamicValueRegex)).toEqual(expected) | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     describe('UpperCaseKeys', () => { | ||||||
|  |         it('should uppercase all first-level keys in an object', () => { | ||||||
|  |             let a = { test: 1, Test2: 2 } | ||||||
|  |             expect(UpperCaseKeys(a)).toEqual({ TEST: 1, TEST2: 2 }) | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     describe('removeOptionalKeys', () => { | ||||||
|  |         it('should remove empty objects from an object', () => { | ||||||
|  |             let a = { b: {}, c: 2, d: {} } | ||||||
|  |             expect(removeOptionalKeys(a, ['b', 'd'])).toEqual({ c: 2 }) | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     describe('toKebabCase', () => { | ||||||
|  |         it('should convert camel case to kebab case', () => { | ||||||
|  |             expect(toKebabCase('helloWorld')).toBe('hello-world') | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     describe('replaceInObject', () => { | ||||||
|  |         it('should replace every value in an object with the output of a function', () => { | ||||||
|  |             let a = { b: 'b', c: 'c' } | ||||||
|  |             expect(replaceInObject(a, (obj) => 'a')).toEqual({ b: 'a', c: 'a' }) | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     describe('expandPath', () => { | ||||||
|  |         test.each([ | ||||||
|  |             ['https://alchem.ee', 'api/v1/hello'], | ||||||
|  |             ['https://alchem.ee/', '/api/v1/hello'], | ||||||
|  |             ['https://alchem.ee', '/api/v1/hello'], | ||||||
|  |             ['https://alchem.ee/', 'api/v1/hello'] | ||||||
|  |         ])( | ||||||
|  |             'should add a base url to the path is the path is not a url: %s, %s', | ||||||
|  |             (url, path) => { | ||||||
|  |                 expect(expandPath(url, path)).toEqual( | ||||||
|  |                     'https://alchem.ee/api/v1/hello' | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         it('should return the path if its a fully fledged url on its own', () => { | ||||||
|  |             expect( | ||||||
|  |                 expandPath('https://alchem.ee', 'https://martianwabbit.com') | ||||||
|  |             ).toEqual('https://martianwabbit.com') | ||||||
|  |         }) | ||||||
|  |     }) | ||||||
|  | }) | ||||||
							
								
								
									
										22
									
								
								src/beau.js
								
								
								
								
							
							
						
						
									
										22
									
								
								src/beau.js
								
								
								
								
							|  | @ -1,12 +1,20 @@ | ||||||
| const RequestList = require('./requestList'); | const RequestList = require('./requestList') | ||||||
| const Config = require('./config'); | const Config = require('./config') | ||||||
|  | const { moduleVersion } = require('./shared') | ||||||
| 
 | 
 | ||||||
| class Beau { | class Beau { | ||||||
| 	constructor(doc) { |     constructor(doc, env = {}) { | ||||||
| 		this.config = new Config(doc); |         this.config = new Config(doc, env) | ||||||
|  |         this.requests = new RequestList(this.config) | ||||||
| 
 | 
 | ||||||
| 		this.requests = new RequestList(this.config.requests, this.config); |         if (this.config.VERSION !== moduleVersion()) { | ||||||
| 	} |             console.warn( | ||||||
|  |                 `This Beau file expects v${ | ||||||
|  |                     this.config.VERSION | ||||||
|  |                 }. You are using v${moduleVersion()}.` | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = Beau; | module.exports = Beau | ||||||
|  |  | ||||||
							
								
								
									
										146
									
								
								src/config.js
								
								
								
								
							
							
						
						
									
										146
									
								
								src/config.js
								
								
								
								
							|  | @ -1,92 +1,100 @@ | ||||||
| const deepMerge = require('deepmerge'); | const deepMerge = require('deepmerge') | ||||||
| const { requestRegex, UpperCaseKeys } = require('./shared'); | const { requestRegex, UpperCaseKeys, moduleVersion } = require('./shared') | ||||||
|  | const Plugins = require('./plugins') | ||||||
| 
 | 
 | ||||||
| class Config { | class Config { | ||||||
| 	constructor(doc) { |     constructor(doc, env = {}) { | ||||||
| 		this.defaultConfigValues = { |         const defaultConfigValues = { | ||||||
| 			VERSION: 1, |             VERSION: moduleVersion(), | ||||||
| 			CACHE: false, |             ENDPOINT: '', | ||||||
| 			ENDPOINT: '', |             PLUGINS: [], | ||||||
| 			PLUGINS: [], |             DEFAULTS: {}, | ||||||
| 			DEFAULTS: {}, |             ENVIRONMENT: {}, | ||||||
| 			ENVIRONMENT: {}, |             HOSTS: [], | ||||||
| 			HOSTS: [] |             COOKIEJAR: false | ||||||
| 		}; |         } | ||||||
| 
 | 
 | ||||||
| 		this.configKeys = Object.keys(this.defaultConfigValues); |         this.configKeys = Object.keys(defaultConfigValues) | ||||||
| 		this.doc = doc; |  | ||||||
| 
 | 
 | ||||||
| 		let config = this.loadConfig(doc); |         let config = this.loadConfig(doc) | ||||||
| 		this.configKeys.forEach(k => { |         Object.assign(this, defaultConfigValues, config) | ||||||
| 			this[k] = config[k] || this.defaultConfigValues[k]; |  | ||||||
| 		}); |  | ||||||
| 
 | 
 | ||||||
| 		this.requests = []; |         this.ENVIRONMENT = deepMerge(this.ENVIRONMENT, env) | ||||||
| 
 | 
 | ||||||
| 		this.loadRequests(doc, { |         this.REQUESTS = [] | ||||||
| 			DEFAULTS: this.DEFAULTS, |  | ||||||
| 			ENDPOINT: this.ENDPOINT |  | ||||||
| 		}); |  | ||||||
| 
 | 
 | ||||||
| 		this.loadHosts(this.HOSTS, config); |         this.loadRequests(doc, { | ||||||
| 	} |             DEFAULTS: this.DEFAULTS, | ||||||
|  |             ENDPOINT: this.ENDPOINT | ||||||
|  |         }) | ||||||
| 
 | 
 | ||||||
| 	loadHosts(hosts, rootConfig) { |         this.loadHosts(this.HOSTS, config, defaultConfigValues) | ||||||
| 		hosts.forEach(host => { |  | ||||||
| 			if (typeof host.host === 'undefined') { |  | ||||||
| 				throw new Error(`Host doesn't indicate it's host name.`); |  | ||||||
| 			} |  | ||||||
| 
 | 
 | ||||||
| 			let config = deepMerge( |         this.PLUGINS = new Plugins(this.PLUGINS) | ||||||
| 				this.defaultConfigValues, |     } | ||||||
| 				this.loadConfig(host) |  | ||||||
| 			); |  | ||||||
| 
 | 
 | ||||||
| 			config.DEFAULTS = deepMerge(rootConfig.DEFAULTS, config.DEFAULTS); |     loadHosts(hosts, rootConfig, defaultConfigValues) { | ||||||
|  |         hosts.forEach((host) => { | ||||||
|  |             if (typeof host.host === 'undefined') { | ||||||
|  |                 throw new Error(`Host doesn't indicate it's host name.`) | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
| 			this.loadRequests(host, { |             let config = deepMerge(defaultConfigValues, this.loadConfig(host)) | ||||||
| 				DEFAULTS: config.DEFAULTS, |  | ||||||
| 				ENDPOINT: config.ENDPOINT, |  | ||||||
| 				NAMESPACE: host.host |  | ||||||
| 			}); |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	loadRequests(host, settings) { |             config.DEFAULTS = deepMerge(rootConfig.DEFAULTS, config.DEFAULTS) | ||||||
| 		let requests = Object.keys(host) |  | ||||||
| 			.filter(key => requestRegex.test(key)) |  | ||||||
| 			.map(key => { |  | ||||||
| 				let requestDefinitionIsString = typeof host[key] === 'string'; |  | ||||||
| 				let originalRequest = requestDefinitionIsString |  | ||||||
| 					? { ALIAS: host[key] } |  | ||||||
| 					: deepMerge.all([host[key]]); |  | ||||||
| 
 | 
 | ||||||
| 				let request = UpperCaseKeys(originalRequest); |             this.loadRequests(host, { | ||||||
|  |                 DEFAULTS: config.DEFAULTS, | ||||||
|  |                 ENDPOINT: config.ENDPOINT, | ||||||
|  |                 NAMESPACE: host.host | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 				if (settings.NAMESPACE) { |     loadRequests(host, settings) { | ||||||
| 					request.ALIAS = `${settings.NAMESPACE}:${request.ALIAS}`; |         Object.entries(host) | ||||||
| 				} |             .filter(([key]) => requestRegex.test(key)) | ||||||
|  |             .forEach(([key, rDefinition]) => { | ||||||
|  |                 if (Array.isArray(rDefinition)) { | ||||||
|  |                     rDefinition.forEach((req) => | ||||||
|  |                         this.addRequest(key, req, settings) | ||||||
|  |                     ) | ||||||
|  |                 } else { | ||||||
|  |                     this.addRequest(key, rDefinition, settings) | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 				request.REQUEST = key; |     addRequest(key, rDefinition, settings) { | ||||||
| 				request.ENDPOINT = settings.ENDPOINT; |         let requestDefinitionIsString = typeof rDefinition === 'string' | ||||||
|  |         let originalRequest = requestDefinitionIsString | ||||||
|  |             ? { ALIAS: rDefinition } | ||||||
|  |             : rDefinition | ||||||
| 
 | 
 | ||||||
| 				let defaults = UpperCaseKeys(settings.DEFAULTS); |         let request = UpperCaseKeys(originalRequest) | ||||||
| 
 | 
 | ||||||
| 				return deepMerge(defaults, request); |         if (settings.NAMESPACE) { | ||||||
| 			}); |             request.ALIAS = `${settings.NAMESPACE}:${request.ALIAS}` | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
| 		this.requests = this.requests.concat(requests); |         request.REQUEST = key | ||||||
| 	} |         request.COOKIEJAR = this.COOKIEJAR | ||||||
|  |         request.ENDPOINT = settings.ENDPOINT | ||||||
| 
 | 
 | ||||||
| 	loadConfig(host) { |         let defaults = UpperCaseKeys(settings.DEFAULTS) | ||||||
| 		let config = {}; |  | ||||||
| 
 | 
 | ||||||
| 		Object.keys(host) |         this.REQUESTS.push(deepMerge(defaults, request)) | ||||||
| 			.filter(k => this.configKeys.includes(k.toUpperCase())) |     } | ||||||
| 			.forEach(k => (config[k.toUpperCase()] = host[k])); |  | ||||||
| 
 | 
 | ||||||
| 		return config; |     loadConfig(host) { | ||||||
| 	} |         let config = {} | ||||||
|  | 
 | ||||||
|  |         Object.entries(host) | ||||||
|  |             .filter(([key]) => this.configKeys.includes(key.toUpperCase())) | ||||||
|  |             .forEach(([key, value]) => (config[key.toUpperCase()] = value)) | ||||||
|  | 
 | ||||||
|  |         return config | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = Config; | module.exports = Config | ||||||
|  |  | ||||||
|  | @ -0,0 +1,119 @@ | ||||||
|  | const vm = require('vm') | ||||||
|  | const requireg = require('requireg') | ||||||
|  | const deepmerge = require('deepmerge') | ||||||
|  | const { toKebabCase, dynamicValueRegex, replaceInObject } = require('./shared') | ||||||
|  | const { isPlainObject } = require('is-plain-object') | ||||||
|  | 
 | ||||||
|  | class Plugins { | ||||||
|  |     constructor(plugins = [], autoload = ['std']) { | ||||||
|  |         this.registry = { | ||||||
|  |             preRequestModifiers: [], | ||||||
|  |             postRequestModifiers: [], | ||||||
|  |             dynamicValues: [] | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.context = {} | ||||||
|  | 
 | ||||||
|  |         this.autoload = autoload | ||||||
|  | 
 | ||||||
|  |         this.loadPlugins(plugins.concat(this.autoload)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     normalizePlugins(plugins) { | ||||||
|  |         let results = {} | ||||||
|  | 
 | ||||||
|  |         plugins.forEach((plugin) => { | ||||||
|  |             let name = plugin | ||||||
|  |             let settings = undefined | ||||||
|  | 
 | ||||||
|  |             if (typeof plugin === 'object') { | ||||||
|  |                 let keys = Object.keys(plugin) | ||||||
|  | 
 | ||||||
|  |                 if (keys.length !== 1) { | ||||||
|  |                     throw new Error(`Plugin items should contain only one key.`) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 name = keys[0] | ||||||
|  |                 settings = plugin[name] | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             results[name] = settings | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         return results | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     loadPlugins(plugins) { | ||||||
|  |         plugins = this.normalizePlugins(plugins) | ||||||
|  |         Object.keys(plugins).forEach((name) => { | ||||||
|  |             const module = `beau-${toKebabCase(name)}` | ||||||
|  | 
 | ||||||
|  |             if (typeof requireg.resolve(module) !== 'undefined') { | ||||||
|  |                 const plugin = requireg(module) | ||||||
|  |                 new plugin(this, plugins[name]) | ||||||
|  |             } else { | ||||||
|  |                 if (this.autoload.includes(name)) return | ||||||
|  | 
 | ||||||
|  |                 console.warn( | ||||||
|  |                     `Plugin ${name} couldn't be found. It is available globally?` | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     executeModifier(modifier, obj, orig) { | ||||||
|  |         let result = deepmerge({}, obj, { isMergeableObject: isPlainObject }) | ||||||
|  | 
 | ||||||
|  |         this.registry[modifier].forEach( | ||||||
|  |             (modifier) => (result = modifier(result, orig)) | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         return result | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     replaceDynamicValues(obj) { | ||||||
|  |         vm.createContext(this.context) | ||||||
|  |         return replaceInObject(obj, (val) => { | ||||||
|  |             let valIsEmpty = val.trim().length === 0 | ||||||
|  | 
 | ||||||
|  |             if (valIsEmpty) { | ||||||
|  |                 return val | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |                 let onlyHasDynamic = | ||||||
|  |                     val.replace(dynamicValueRegex, '').trim() === '' | ||||||
|  | 
 | ||||||
|  |                 if (onlyHasDynamic) { | ||||||
|  |                     let call | ||||||
|  |                     val.replace(dynamicValueRegex, (match, c) => { | ||||||
|  |                         call = c | ||||||
|  |                     }) | ||||||
|  | 
 | ||||||
|  |                     return vm.runInContext(call, this.context) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 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 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = Plugins | ||||||
							
								
								
									
										200
									
								
								src/request.js
								
								
								
								
							
							
						
						
									
										200
									
								
								src/request.js
								
								
								
								
							|  | @ -1,119 +1,131 @@ | ||||||
| const request = require('request-promise-native'); | const request = require('request-promise-native') | ||||||
|  | const RequestCache = require('./requestCache') | ||||||
|  | const Plugins = require('./plugins') | ||||||
|  | 
 | ||||||
| const { | const { | ||||||
| 	httpVerbs, |     requestRegex, | ||||||
| 	requestRegex, |     replacementRegex, | ||||||
| 	replacementRegex, |     UpperCaseKeys, | ||||||
| 	UpperCaseKeys |     removeOptionalKeys, | ||||||
| } = require('./shared'); |     isUrl | ||||||
| const RequestList = require('./requestList'); | } = require('./shared') | ||||||
| const RequestCache = require('./requestCache'); |  | ||||||
| 
 | 
 | ||||||
| class Request { | class Request { | ||||||
| 	constructor(req) { |     constructor(req, plugins = new Plugins()) { | ||||||
| 		this.originalRequest = req; |         this.originalRequest = req | ||||||
|  |         this.plugins = plugins | ||||||
| 
 | 
 | ||||||
| 		const { |         req = UpperCaseKeys(req) | ||||||
| 			REQUEST, |         Object.assign(this, req) | ||||||
| 			ALIAS, |  | ||||||
| 			PAYLOAD, |  | ||||||
| 			ENDPOINT, |  | ||||||
| 			PARAMS, |  | ||||||
| 			HEADERS |  | ||||||
| 		} = UpperCaseKeys(req); |  | ||||||
| 
 | 
 | ||||||
| 		if (!ALIAS) { |         if (!this.ALIAS) { | ||||||
| 			throw new Error(`${REQUEST} is missing an alias.`); |             throw new Error(`${this.REQUEST} is missing an alias.`) | ||||||
| 		} |         } | ||||||
| 
 | 
 | ||||||
| 		const { verb, path } = this.parseRequest(REQUEST); |         const { VERB, PATH } = this.parseRequest(this.REQUEST) | ||||||
| 
 | 
 | ||||||
| 		this.VERB = verb; |         this.VERB = VERB | ||||||
| 		this.ENDPOINT = ENDPOINT + path; |         this.PATH = PATH | ||||||
| 
 | 
 | ||||||
| 		this.HEADERS = HEADERS; |         this.DEPENDENCIES = this.findDependencies(req) | ||||||
| 		this.PAYLOAD = PAYLOAD; |     } | ||||||
| 		this.PARAMS = PARAMS; |  | ||||||
| 
 | 
 | ||||||
| 		this.ALIAS = ALIAS; |     parseRequest(request) { | ||||||
|  |         const parts = request.match(requestRegex) | ||||||
| 
 | 
 | ||||||
| 		this.DEPENDENCIES = this.findDependencies(req); |         return { | ||||||
| 	} |             VERB: parts[1], | ||||||
|  |             PATH: parts[2] | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 	parseRequest(request) { |     findDependencies(request, set = new Set()) { | ||||||
| 		const parts = request.match(requestRegex); |         let type = typeof request | ||||||
| 
 | 
 | ||||||
| 		return { |         if (type === 'object' && request !== null) { | ||||||
| 			verb: parts[1], |             Object.entries(request) | ||||||
| 			path: parts[2] |                 .filter(([key]) => key !== 'ALIAS') | ||||||
| 		}; |                 .forEach(([key, value]) => { | ||||||
| 	} |                     set = this.findDependencies(value, set) | ||||||
|  |                 }) | ||||||
|  |         } else if (type === 'string') { | ||||||
|  |             const matches = [] | ||||||
|  |             request.replace( | ||||||
|  |                 replacementRegex, | ||||||
|  |                 (match, g1) => !match.startsWith('\\') && matches.push(g1) | ||||||
|  |             ) | ||||||
| 
 | 
 | ||||||
| 	findDependencies(request, set = new Set()) { |             const deps = matches.map((m) => m.split('.')[0]) | ||||||
| 		if (typeof request === 'object') { |  | ||||||
| 			const keys = Object.keys(request).filter(key => key !== 'ALIAS'); |  | ||||||
| 
 | 
 | ||||||
| 			keys.forEach(key => { |             return new Set([...set, ...deps]) | ||||||
| 				set = this.findDependencies(request[key], set); |         } | ||||||
| 			}); |  | ||||||
| 		} else if (typeof request === 'string') { |  | ||||||
| 			const matches = request.match(replacementRegex) || []; |  | ||||||
| 			const deps = matches.map(m => m.split('.')[0].substring(1)); |  | ||||||
| 
 | 
 | ||||||
| 			return new Set([...set, ...deps]); |         return set | ||||||
| 		} |     } | ||||||
| 
 | 
 | ||||||
| 		return set; |     async exec(cache = new RequestCache()) { | ||||||
| 	} |         let settings = cache.parse({ | ||||||
|  |             baseUrl: '', | ||||||
|  |             uri: this.PATH, | ||||||
|  |             method: this.VERB, | ||||||
|  |             jar: this.COOKIEJAR, | ||||||
| 
 | 
 | ||||||
| 	async exec(modifiers = [], cache = new RequestCache()) { |             headers: this.HEADERS, | ||||||
| 		const settings = { |             qs: this.PARAMS, | ||||||
| 			endpoint: cache.parse(this.ENDPOINT), |             body: this.PAYLOAD, | ||||||
| 			method: this.VERB, |             form: this.FORM, | ||||||
| 			headers: cache.parse(this.HEADERS), |             formData: this.FORMDATA, | ||||||
| 			query: cache.parse(this.PARAMS), |  | ||||||
| 			payload: cache.parse(this.PAYLOAD) |  | ||||||
| 		}; |  | ||||||
| 
 | 
 | ||||||
| 		modifiers.forEach(mod => { |             json: true, | ||||||
| 			if (typeof mod.preRequest !== 'undefined') { |             simple: false, | ||||||
| 				mod.preRequest(settings, this.originalRequest); |             resolveWithFullResponse: true | ||||||
| 			} |         }) | ||||||
| 		}); |  | ||||||
| 
 | 
 | ||||||
| 		try { |         const isPathFullUrl = isUrl(settings.uri) | ||||||
| 			const response = await request({ |         settings.baseUrl = isPathFullUrl ? '' : this.ENDPOINT | ||||||
| 				url: settings.endpoint, |  | ||||||
| 				method: settings.method, |  | ||||||
| 				headers: settings.headers, |  | ||||||
| 				qs: settings.query, |  | ||||||
| 				body: settings.payload, |  | ||||||
| 
 | 
 | ||||||
| 				json: true, |         settings = removeOptionalKeys(settings, [ | ||||||
| 				simple: false, |             'headers', | ||||||
| 				resolveWithFullResponse: true |             'qs', | ||||||
| 			}); |             'body', | ||||||
|  |             'form', | ||||||
|  |             'formData' | ||||||
|  |         ]) | ||||||
| 
 | 
 | ||||||
| 			const results = { |         settings = this.plugins.replaceDynamicValues(settings) | ||||||
| 				request: { |  | ||||||
| 					headers: response.request.headers, |  | ||||||
| 					body: response.request.body, |  | ||||||
| 					endpoint: response.request.uri.href |  | ||||||
| 				}, |  | ||||||
| 				response: { |  | ||||||
| 					status: response.statusCode, |  | ||||||
| 					headers: response.headers, |  | ||||||
| 					body: response.body |  | ||||||
| 				}, |  | ||||||
| 				body: response.body |  | ||||||
| 			}; |  | ||||||
| 
 | 
 | ||||||
| 			cache.add(`$${this.ALIAS}`, results); |         settings = this.plugins.executeModifier( | ||||||
|  |             'preRequestModifiers', | ||||||
|  |             settings, | ||||||
|  |             this.originalRequest | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
| 			return results; |         const response = await request(settings) | ||||||
| 		} catch ({ error }) { | 
 | ||||||
| 			throw new Error(error); |         let results = { | ||||||
| 		} |             request: { | ||||||
| 	} |                 headers: response.request.headers, | ||||||
|  |                 body: response.request.body, | ||||||
|  |                 endpoint: response.request.uri.href | ||||||
|  |             }, | ||||||
|  |             response: { | ||||||
|  |                 status: response.statusCode, | ||||||
|  |                 headers: response.headers, | ||||||
|  |                 body: response.body | ||||||
|  |             }, | ||||||
|  |             body: response.body | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         results = this.plugins.executeModifier( | ||||||
|  |             'postRequestModifiers', | ||||||
|  |             results, | ||||||
|  |             this.originalRequest | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         cache.add(this.ALIAS, results) | ||||||
|  | 
 | ||||||
|  |         return results | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = Request; | module.exports = Request | ||||||
|  |  | ||||||
|  | @ -1,53 +1,46 @@ | ||||||
| const { replacementRegex } = require('./shared'); | const { replacementRegex, replaceInObject } = require('./shared') | ||||||
| 
 | 
 | ||||||
| class RequestCache { | class RequestCache { | ||||||
| 	constructor() { |     constructor() { | ||||||
| 		this.$cache = {}; |         this.$cache = {} | ||||||
| 	} |     } | ||||||
| 
 | 
 | ||||||
| 	exists(key) { |     exists(key) { | ||||||
| 		return typeof this.$cache[key] !== 'undefined'; |         return typeof this.$cache[key] !== 'undefined' | ||||||
| 	} |     } | ||||||
| 
 | 
 | ||||||
| 	add(key, value) { |     add(key, value) { | ||||||
| 		this.$cache[key] = value; |         this.$cache[key] = value | ||||||
| 	} |     } | ||||||
| 
 | 
 | ||||||
| 	get(path) { |     get(path) { | ||||||
| 		let result = this.$cache; |         let result = this.$cache | ||||||
| 		path.split('.').forEach(part => { |         path.split('.').forEach((part) => { | ||||||
| 			result = result[part]; |             if (result[part] === undefined) { | ||||||
| 		}); |                 throw new Error(`${path} not found in cache.`) | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
| 		if (typeof result === 'undefined') { |             result = result[part] | ||||||
| 			throw new Error(`${path} not found in cache: `, path); |         }) | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		return result; |         return result | ||||||
| 	} |     } | ||||||
| 
 | 
 | ||||||
| 	parse(item) { |     parse(item) { | ||||||
| 		let type = typeof item; |         if (item === null) { | ||||||
|  |             return null | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
| 		if (type === 'undefined') { |         return replaceInObject(item, (item) => | ||||||
| 			return {}; |             item.replace(replacementRegex, (match, key) => { | ||||||
| 		} |                 if (match.startsWith('\\')) { | ||||||
|  |                     return match.replace('\\$', '$') | ||||||
|  |                 } | ||||||
| 
 | 
 | ||||||
| 		if (item === null) { |                 return this.get(key) | ||||||
| 			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; |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = RequestCache; | module.exports = RequestCache | ||||||
|  |  | ||||||
|  | @ -1,93 +1,54 @@ | ||||||
| const Request = require('./request'); | const Request = require('./request') | ||||||
| const RequestCache = require('./requestCache'); | const RequestCache = require('./requestCache') | ||||||
| const httpVerbs = require('./shared').httpVerbs; |  | ||||||
| const requireg = require('requireg'); |  | ||||||
| 
 | 
 | ||||||
| class RequestList { | class RequestList { | ||||||
| 	constructor(requests = [], config = {}) { |     constructor(config = { REQUESTS: [] }) { | ||||||
| 		this.config = config; |         this.list = this.loadRequests(config.REQUESTS, config.PLUGINS) | ||||||
| 		this.requests = requests; |         this.cache = new RequestCache() | ||||||
| 
 | 
 | ||||||
| 		this.modifiers = this.loadPlugins(); |         this.cache.add(`env`, config.ENVIRONMENT) | ||||||
| 		this.list = this.loadRequests(); |     } | ||||||
| 		this.cache = new RequestCache(); |  | ||||||
| 
 | 
 | ||||||
| 		this.cache.add(`$env`, this.config.ENVIRONMENT); |     async execByAlias(alias) { | ||||||
| 	} |         if (this.cache.exists(alias)) { | ||||||
|  |             return this.cache.get(alias) | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
| 	async execByAlias(alias) { |         const request = this.list.find((r) => r.ALIAS === alias) | ||||||
| 		if (this.cache.exists(`$${alias}`)) { |  | ||||||
| 			return this.applyPostResponseModifiers(this.cache.get(`$${alias}`)); |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		const request = this.list.find(r => r.ALIAS === alias); |         if (typeof request === 'undefined') { | ||||||
|  |             throw new Error(`${alias} not found among the requests.`) | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
| 		if (typeof request === 'undefined') { |         try { | ||||||
| 			throw new Error(`${alias} not found among the requests.`); |             await this.fetchDependencies(Array.from(request.DEPENDENCIES)) | ||||||
| 		} |             return await request.exec(this.cache) | ||||||
|  |         } catch (reason) { | ||||||
|  |             throw new Error( | ||||||
|  |                 `Request ${request.VERB} ${request.ENDPOINT} FAILED. \n${reason}` | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 		try { |     async fetchDependencies(dependencies) { | ||||||
| 			await this.fetchDependencies(Array.from(request.DEPENDENCIES)); |         dependencies = dependencies.map((d) => this.execByAlias(d)) | ||||||
| 			const response = await request.exec(this.modifiers, this.cache); |         await Promise.all(dependencies) | ||||||
| 
 | 
 | ||||||
| 			return this.applyPostResponseModifiers(response); |         return this.cache | ||||||
| 		} catch (reason) { |     } | ||||||
| 			throw new Error( |  | ||||||
| 				`Request: ${request.VERB} ${ |  | ||||||
| 					request.ENDPOINT |  | ||||||
| 				} FAILED. \n${reason}` |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	async fetchDependencies(dependencies) { |     loadRequests(REQUESTS, PLUGINS) { | ||||||
| 		dependencies = dependencies.map(d => this.execByAlias(d)); |         let requests = [] | ||||||
| 		await Promise.all(dependencies); |         REQUESTS.forEach((request) => { | ||||||
|  |             try { | ||||||
|  |                 requests.push(new Request(request, PLUGINS)) | ||||||
|  |             } catch (e) { | ||||||
|  |                 throw new Error(`${request.request} was ignored: ${e}`) | ||||||
|  |             } | ||||||
|  |         }) | ||||||
| 
 | 
 | ||||||
| 		return this.cache; |         return requests | ||||||
| 	} |     } | ||||||
| 
 |  | ||||||
| 	loadRequests() { |  | ||||||
| 		let requests = []; |  | ||||||
| 		this.requests.forEach(request => { |  | ||||||
| 			try { |  | ||||||
| 				let r = new Request(request); |  | ||||||
| 				requests.push(r); |  | ||||||
| 			} catch (e) { |  | ||||||
| 				throw new Error(`${request.request} was ignored: ${e}`); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		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; | module.exports = RequestList | ||||||
|  |  | ||||||
							
								
								
									
										116
									
								
								src/shared.js
								
								
								
								
							
							
						
						
									
										116
									
								
								src/shared.js
								
								
								
								
							|  | @ -1,27 +1,99 @@ | ||||||
|  | const { URL } = require('url') | ||||||
|  | 
 | ||||||
| const httpVerbs = [ | const httpVerbs = [ | ||||||
| 	'GET', |     'GET', | ||||||
| 	'HEAD', |     'HEAD', | ||||||
| 	'POST', |     'POST', | ||||||
| 	'PUT', |     'PUT', | ||||||
| 	'DELETE', |     'DELETE', | ||||||
| 	'CONNECT', |     'CONNECT', | ||||||
| 	'OPTIONS', |     'OPTIONS', | ||||||
| 	'TRACE', |     'TRACE', | ||||||
| 	'PATCH' |     'PATCH' | ||||||
| ]; | ] | ||||||
| 
 | 
 | ||||||
| const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i'); | 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) { | const UpperCaseKeys = function (obj) { | ||||||
| 	let result = {}; |     let result = {} | ||||||
| 	Object.keys(obj).forEach(k => (result[k.toUpperCase()] = obj[k])); |     Object.entries(obj).forEach(([k, v]) => (result[k.toUpperCase()] = v)) | ||||||
| 	return result; |     return result | ||||||
| }; | } | ||||||
|  | 
 | ||||||
|  | const isEmptyObject = (obj) => | ||||||
|  |     Object.keys(obj).length === 0 && obj.constructor === Object | ||||||
|  | 
 | ||||||
|  | const removeOptionalKeys = function (obj, optionalValues) { | ||||||
|  |     let result = {} | ||||||
|  | 
 | ||||||
|  |     Object.entries(obj).forEach(([key, value]) => { | ||||||
|  |         if (optionalValues.includes(key) && isEmptyObject(value)) { | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         result[key] = value | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const toKebabCase = function (str) { | ||||||
|  |     return str | ||||||
|  |         .trim() | ||||||
|  |         .replace(/([a-z])([A-Z])/g, '$1-$2') | ||||||
|  |         .toLowerCase() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const replaceInObject = function (obj, fn) { | ||||||
|  |     if (obj === null) { | ||||||
|  |         return null | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch (typeof obj) { | ||||||
|  |         case 'undefined': | ||||||
|  |             return {} | ||||||
|  |         case 'string': | ||||||
|  |             return fn(obj) | ||||||
|  |         case 'object': | ||||||
|  |             obj = Object.assign({}, obj) | ||||||
|  |             Object.entries(obj).forEach( | ||||||
|  |                 ([key, value]) => (obj[key] = replaceInObject(value, fn)) | ||||||
|  |             ) | ||||||
|  |         default: | ||||||
|  |             return obj | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const moduleVersion = () => parseInt(require('../package.json').version, 10) | ||||||
|  | 
 | ||||||
|  | const isUrl = function (str) { | ||||||
|  |     try { | ||||||
|  |         new URL(str) | ||||||
|  |         return true | ||||||
|  |     } catch (e) { | ||||||
|  |         return false | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const expandPath = (url, path) => { | ||||||
|  |     if (isUrl(path)) { | ||||||
|  |         return path | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return url.replace(/\/+$/, '') + '/' + path.replace(/^\/+/, '') | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | module.exports = { | ||||||
| 	httpVerbs, |     requestRegex, | ||||||
| 	requestRegex, |     replacementRegex, | ||||||
| 	replacementRegex, |     dynamicValueRegex, | ||||||
| 	UpperCaseKeys |     UpperCaseKeys, | ||||||
| }; |     removeOptionalKeys, | ||||||
|  |     toKebabCase, | ||||||
|  |     replaceInObject, | ||||||
|  |     moduleVersion, | ||||||
|  |     isUrl, | ||||||
|  |     expandPath | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue