diff --git a/LICENSE b/LICENSE index 8da5195..65af427 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ -Copyright 2017 David Sergio Díaz +Copyright 2018 David Sergio 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. \ No newline at end of file +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. diff --git a/README.md b/README.md index b06e76a..9d2d71a 100644 --- a/README.md +++ b/README.md @@ -12,63 +12,82 @@ ## What is Beau? -Beau, is a CLI that executes HTTP requests based on a YAML configuration file. This makes testing easy, it allows you to share test requests with others as part of your repo. +Beau, is a CLI that executes HTTP requests based on a YAML configuration file. +This makes testing easy, it allows you to share test requests with others as +part of your repo. ![A Gif showing how beau works](http://files.martianwabbit.com/beau2.gif) ## Installation - npm install -g beau + + npm install -g beau ## Usage - ⚡ beau --help - Usage: beau [options] [command] + ⚡ beau --help + + Usage: beau [options] [command] - Options: + Options: - -V, --version output the version number - -h, --help output usage information + -V, --version output the version number + -h, --help output usage information - Commands: + Commands: - request [options] - list [options] + request [options] + list [options] ## Example Configuration File - version: 1 - endpoint: https://example.com/api/ + version: 1 + endpoint: https://example.com/api/ - POST /session: - ALIAS: session - PAYLOAD: - username: seich - password: hello01 + POST /session: + ALIAS: session + PAYLOAD: + username: seich + password: hello01 - GET /profile - ALIAS: profile - HEADERS: - authorization: Bearer $session.response.body.token + GET /profile + ALIAS: profile + HEADERS: + authorization: Bearer $session.response.body.token - GET /user/$profile.response.body.id/posts - ALIAS: friends - HEADERS: - authorization: Bearer $session.response.body.token - PARAMS: - archived: true + GET /user/$profile.response.body.id/posts + ALIAS: friends + HEADERS: + authorization: Bearer $session.response.body.token + PARAMS: + archived: true ## Example Usage - beau request profile -That would execute the profile request along with it´s dependencies. In this case, the session request would be made as well since we are using it´s response value as part of our current request. + beau request profile + +That would execute the profile request along with it´s dependencies. In this +case, the session request would be made as well since we are using it´s response +value as part of our current request. ## License -Copyright 2017 David Sergio 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: +Copyright 2018 David Sergio Díaz -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +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 "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. +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. diff --git a/bin/beau b/bin/beau index afa9fc4..8923dbe 100755 --- a/bin/beau +++ b/bin/beau @@ -20,7 +20,7 @@ program 'beau.yml' ) .option( - '-v --verbose', + '--verbose', 'Show all the information available on the current request.', false ) diff --git a/examples/hosts.yml b/examples/hosts.yml new file mode 100644 index 0000000..2f754e9 --- /dev/null +++ b/examples/hosts.yml @@ -0,0 +1,32 @@ +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 + + + diff --git a/package-lock.json b/package-lock.json index 679d1f3..e4e21a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1145,910 +1145,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", - "dev": true, - "optional": true, - "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3087,7 +2183,6 @@ "anymatch": "1.3.2", "exec-sh": "0.2.1", "fb-watchman": "2.0.0", - "fsevents": "1.1.3", "minimatch": "3.0.4", "minimist": "1.2.0", "walker": "1.0.7", @@ -3779,7 +2874,6 @@ "anymatch": "1.3.2", "exec-sh": "0.2.1", "fb-watchman": "2.0.0", - "fsevents": "1.1.3", "minimatch": "3.0.4", "minimist": "1.2.0", "walker": "1.0.7", diff --git a/src/__tests__/__snapshots__/beau.spec.js.snap b/src/__tests__/__snapshots__/beau.spec.js.snap index b1abe4f..b8f736f 100644 --- a/src/__tests__/__snapshots__/beau.spec.js.snap +++ b/src/__tests__/__snapshots__/beau.spec.js.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Beau's config Loader. should set up defaults for all requests 1`] = ` +exports[`Beau's config Loader. should create a request list 1`] = ` Beau { - "config": Object { + "config": Config { "CACHE": false, "DEFAULTS": Object { "headers": Object { @@ -11,28 +11,62 @@ Beau { }, "ENDPOINT": "http://jsonplaceholder.typicode.com", "ENVIRONMENT": Object {}, + "HOSTS": Array [], "PLUGINS": Array [], "VERSION": 1, - }, - "configKeys": Array [ - "VERSION", - "CACHE", - "ENDPOINT", - "PLUGINS", - "DEFAULTS", - "ENVIRONMENT", - ], - "defaults": Object { - "CACHE": false, - "DEFAULTS": Object { - "headers": Object { - "authentication": "hello", - }, + "configKeys": Array [ + "VERSION", + "CACHE", + "ENDPOINT", + "PLUGINS", + "DEFAULTS", + "ENVIRONMENT", + "HOSTS", + ], + "defaultConfigValues": Object { + "CACHE": false, + "DEFAULTS": Object {}, + "ENDPOINT": "", + "ENVIRONMENT": Object {}, + "HOSTS": Array [], + "PLUGINS": Array [], + "VERSION": 1, }, - "ENDPOINT": "http://jsonplaceholder.typicode.com", - "ENVIRONMENT": Object {}, - "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 { "cache": RequestCache { @@ -40,7 +74,7 @@ Beau { "$env": Object {}, }, }, - "config": Object { + "config": Config { "CACHE": false, "DEFAULTS": Object { "headers": Object { @@ -49,14 +83,67 @@ Beau { }, "ENDPOINT": "http://jsonplaceholder.typicode.com", "ENVIRONMENT": Object {}, + "HOSTS": Array [], "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 { "ALIAS": "get-post", "DEPENDENCIES": Set {}, - "DOCUMENTATION": undefined, "ENDPOINT": "http://jsonplaceholder.typicode.com/posts/1", "HEADERS": Object { "authentication": "hello", @@ -67,16 +154,15 @@ Beau { "originalRequest": Object { "ALIAS": "get-post", "ENDPOINT": "http://jsonplaceholder.typicode.com", - "headers": Object { + "HEADERS": Object { "authentication": "hello", }, - "request": "GET /posts/1", + "REQUEST": "GET /posts/1", }, }, Request { "ALIAS": "user", "DEPENDENCIES": Set {}, - "DOCUMENTATION": undefined, "ENDPOINT": "http://jsonplaceholder.typicode.com/user", "HEADERS": Object { "authentication": "hello", @@ -86,17 +172,36 @@ Beau { "PAYLOAD": undefined, "VERB": "GET", "originalRequest": Object { + "ALIAS": "user", "ENDPOINT": "http://jsonplaceholder.typicode.com", - "alias": "user", - "headers": Object { + "HEADERS": Object { "authentication": "hello", "hello": "world", }, - "request": "GET /user", + "REQUEST": "GET /user", }, }, ], "modifiers": Array [], + "requests": Array [ + Object { + "ALIAS": "get-post", + "ENDPOINT": "http://jsonplaceholder.typicode.com", + "HEADERS": Object { + "authentication": "hello", + }, + "REQUEST": "GET /posts/1", + }, + Object { + "ALIAS": "user", + "ENDPOINT": "http://jsonplaceholder.typicode.com", + "HEADERS": Object { + "authentication": "hello", + "hello": "world", + }, + "REQUEST": "GET /user", + }, + ], }, } `; diff --git a/src/__tests__/__snapshots__/config.spec.js.snap b/src/__tests__/__snapshots__/config.spec.js.snap new file mode 100644 index 0000000..dc9ee9c --- /dev/null +++ b/src/__tests__/__snapshots__/config.spec.js.snap @@ -0,0 +1,228 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Config should load multiple hosts 1`] = ` +Config { + "CACHE": false, + "DEFAULTS": Object { + "HEADERS": Object { + "hello": "mars", + }, + }, + "ENDPOINT": "http://example.org", + "ENVIRONMENT": Object {}, + "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", + }, + ], + "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 /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", + }, + ], + }, + "requests": Array [ + Object { + "ALIAS": "e1", + "ENDPOINT": "http://example.org", + "HEADERS": Object { + "hello": "mars", + }, + "REQUEST": "GET /e1", + }, + Object { + "ALIAS": "com:e2", + "ENDPOINT": "http://example.com", + "HEADERS": Object { + "hello": "world", + "world": "hello", + }, + "REQUEST": "GET /e2", + }, + Object { + "ALIAS": "com:posts", + "ENDPOINT": "http://example.com", + "HEADERS": Object { + "hello": "world", + "world": "hello", + }, + "REQUEST": "GET /posts", + }, + Object { + "ALIAS": "net:e3", + "ENDPOINT": "http://example.net", + "HEADERS": Object { + "hello": "world", + "world": "bye", + }, + "REQUEST": "GET /e3", + }, + Object { + "ALIAS": "net:posts", + "ENDPOINT": "http://example.net", + "HEADERS": Object { + "hello": "world", + "world": "bye", + }, + "REQUEST": "GET /posts", + }, + Object { + "ALIAS": "info:posts", + "ENDPOINT": "http://example.info", + "HEADERS": Object { + "hello": "mars", + }, + "REQUEST": "GET /posts", + }, + ], +} +`; + +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, + "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", + }, + ], +} +`; diff --git a/src/__tests__/__snapshots__/request.spec.js.snap b/src/__tests__/__snapshots__/request.spec.js.snap index da87575..45cd417 100644 --- a/src/__tests__/__snapshots__/request.spec.js.snap +++ b/src/__tests__/__snapshots__/request.spec.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Request It should execute a request 1`] = ` +exports[`Request should execute a request 1`] = ` Object { "body": "{\\"hello\\": \\"world\\"}", "request": Object { @@ -20,7 +20,7 @@ Object { } `; -exports[`Request It should execute a request 2`] = ` +exports[`Request should execute a request 2`] = ` Object { "body": "{\\"hello\\": \\"world\\"}", "request": Object { @@ -36,7 +36,7 @@ Object { } `; -exports[`Request It should use modifiers 1`] = ` +exports[`Request should use modifiers 1`] = ` Array [ Array [ Object { diff --git a/src/__tests__/beau.spec.js b/src/__tests__/beau.spec.js index b4052e3..e1b4b00 100644 --- a/src/__tests__/beau.spec.js +++ b/src/__tests__/beau.spec.js @@ -2,23 +2,7 @@ const yaml = require('js-yaml'); const Beau = require('../beau'); describe(`Beau's config Loader.`, () => { - it('Should only load valid configuration keys', () => { - const doc = yaml.safeLoad(` - version: 1 - endpoint: http://martianwabbit.com - cache: false - shouldntBeAdded: true - `); - - const beau = new Beau(doc); - - expect(beau.config.ENDPOINT).toBe(doc.endpoint); - expect(beau.config.CACHE).toBe(doc.cache); - expect(beau.config.VERSION).toBe(doc.version); - expect(beau.config.shouldntBeAdded).toBeUndefined(); - }); - - it('should set up defaults for all requests', () => { + it('should create a request list', () => { const doc = yaml.safeLoad(` version: 1 endpoint: 'http://jsonplaceholder.typicode.com' @@ -36,9 +20,7 @@ describe(`Beau's config Loader.`, () => { const beau = new Beau(doc); + expect(beau.requests).toBeDefined(); expect(beau).toMatchSnapshot(); - beau.requests.list.forEach(r => { - expect(r.HEADERS.authentication).toMatch('hello'); - }); }); }); diff --git a/src/__tests__/config.spec.js b/src/__tests__/config.spec.js new file mode 100644 index 0000000..1eaef8c --- /dev/null +++ b/src/__tests__/config.spec.js @@ -0,0 +1,158 @@ +const yaml = require('js-yaml'); +const Config = require('../config'); + +describe('Config', () => { + it('should load valid config keys', () => { + const doc = yaml.safeLoad(` + version: 1 + endpoint: http://martianwabbit.com + cache: false + shouldntBeAdded: true + `); + + const config = new Config(doc); + expect(config.ENDPOINT).toBe(doc.endpoint); + expect(config.CACHE).toBe(doc.cache); + expect(config.VERSION).toBe(doc.version); + expect(config.shouldntBeAdded).toBeUndefined(); + }); + + it('should load requests', () => { + const doc = yaml.safeLoad(` + endpoint: http://example.com + + GET /profile: get-profile + GET /posts: get-posts + GET /posts/1: get-post + GET /user: + alias: user + headers: + hello: world + `); + + const config = new Config(doc); + expect(Object.keys(config.requests).length).toBe(4); + }); + + it('should set up defaults for all requests', () => { + const doc = yaml.safeLoad(` + version: 1 + endpoint: 'http://jsonplaceholder.typicode.com' + + defaults: + HEADERS: + authentication: hello + + GET /posts/1: get-post + GET /user: + alias: user + headers: + hello: world + `); + + const config = new Config(doc); + + expect(config).toMatchSnapshot(); + Object.values(config.requests).forEach(r => { + expect(r.HEADERS.authentication).toMatch('hello'); + }); + }); + + it('should load multiple hosts', () => { + const doc = yaml.safeLoad(` + endpoint: http://example.org + + defaults: + HEADERS: + hello: mars + + GET /e1: e1 + + hosts: + - host: com + endpoint: http://example.com + + defaults: + HEADERS: + hello: world + world: hello + + GET /e2: e2 + GET /posts: posts + + - host: net + endpoint: http://example.net + + defaults: + HEADERS: + hello: world + world: bye + + GET /e3: e3 + GET /posts: posts + + - host: info + endpoint: http://example.info + + GET /posts: posts + `); + + let config = new Config(doc); + + expect(config).toMatchSnapshot(); + }); + + it('should namespace all aliases within an host', () => { + const doc = yaml.safeLoad(` + hosts: + - host: test1 + endpoint: http://example.com + GET /posts: posts + - host: test2 + endpoint: http://example.net + GET /posts: posts + `); + + let config = new Config(doc); + + expect(config.requests[0].ALIAS).toBe('test1:posts'); + expect(config.requests[1].ALIAS).toBe('test2:posts'); + }); + + it(`should throw if host doesn't have a host key`, () => { + const doc = yaml.safeLoad(` + hosts: + - endpoint: http://example.com + GET /posts: posts + + - host: test2 + endpoint: http://example.net + GET /posts: posts + `); + + expect(() => new Config(doc)).toThrow(); + }); + + it(`should merge host settings with global settings`, () => { + const doc = yaml.safeLoad(` + defaults: + headers: + hello: 1 + + hosts: + - host: test + endpoint: http://example.net + GET /posts: posts + + - host: test2 + endpoint: http://example.org + defaults: + headers: false + + GET /posts: posts + `); + + let config = new Config(doc); + expect(config.requests[0].HEADERS.hello).toBe(1); + }); +}); diff --git a/src/__tests__/request.spec.js b/src/__tests__/request.spec.js index 9f9fd1a..bccf391 100644 --- a/src/__tests__/request.spec.js +++ b/src/__tests__/request.spec.js @@ -4,13 +4,14 @@ const RequestList = require('../requestList'); const requestPromiseNativeMock = require('request-promise-native'); describe('Request', () => { - let req; let cache; + let validRequestConfig; + let invalidRequestConfig; let request; let requestWithoutDependencies; beforeEach(() => { - req = { + validRequestConfig = { request: 'POST /user', endpoint: 'http://martianwabbit.com', alias: 'update', @@ -25,11 +26,16 @@ describe('Request', () => { } }; + invalidRequestConfig = { + request: `POST /session`, + endpoint: 'http://martianwabbit.com' + }; + cache = new RequestCache(); cache.add('$session', { token: 'abc123' }); cache.add('$profile', { UserId: 14 }); - request = new Request(req); + request = new Request(validRequestConfig); requestWithoutDependencies = new Request({ endpoint: 'http://martianwabbit.com', request: 'GET /user', @@ -39,33 +45,37 @@ describe('Request', () => { requestPromiseNativeMock.fail = false; }); - test('It should load up the given request', () => { + it('should load up the given request', () => { expect(request.VERB).toBe('POST'); - expect(request.ENDPOINT).toBe(req.endpoint + '/user'); + expect(request.ENDPOINT).toBe(validRequestConfig.endpoint + '/user'); expect(request.HEADERS).toBeDefined(); expect(request.PAYLOAD).toBeDefined(); expect(request.PARAMS).toBeDefined(); }); - test('It should list all of its dependencies', () => { + it('should throw if a given request is invalid', () => { + expect(() => new Request(invalidRequestConfig)).toThrow(); + }); + + it('should list all of its dependencies', () => { expect(request.DEPENDENCIES.size).toBe(2); expect(request.DEPENDENCIES).toContain('session'); expect(request.DEPENDENCIES).toContain('profile'); }); - test('It should execute a request', async () => { + it('should execute a request', async () => { await expect(request.exec([], cache)).resolves.toMatchSnapshot(); await expect( requestWithoutDependencies.exec() ).resolves.toMatchSnapshot(); }); - test('It should throw if the request fails', async () => { + it('should throw if the request fails', async () => { requestPromiseNativeMock.fail = true; await expect(requestWithoutDependencies.exec()).rejects.toThrow(Error); }); - test('It should use modifiers', async () => { + it('should use modifiers', async () => { const preRequest = jest.fn(); const withPreRequest = [{ preRequest }]; diff --git a/src/__tests__/requestList.spec.js b/src/__tests__/requestList.spec.js index 81e2e61..b9fd190 100644 --- a/src/__tests__/requestList.spec.js +++ b/src/__tests__/requestList.spec.js @@ -1,15 +1,28 @@ +const Config = require('../config'); const RequestList = require('../requestList'); const requestPromiseNativeMock = require('request-promise-native'); describe('RequestList', () => { const endpoint = 'http://martianwabbit.com'; + let env = { environmental: true }; const doc = { - 'POST /session': null, - 'Not a Request': null, + ENDPOINT: endpoint, + ENVIRONMENT: env, + PLUGINS: [ + { + 'beau-jwt': { + data: { + secret: 'shhh.', + userId: 412 + } + } + }, + 'beau-document' + ], 'GET /post': { alias: 'get-posts' }, 'POST /user': { alias: 'user', @@ -23,29 +36,13 @@ describe('RequestList', () => { let requests; beforeEach(() => { requestPromiseNativeMock.fail = false; - requests = new RequestList(doc, { - ENDPOINT: endpoint, - ENVIRONMENT: env, - PLUGINS: [ - { - 'beau-jwt': { - data: { - secret: 'shhh.', - userId: 412 - } - } - }, - 'beau-document' - ] - }); + + let config = new Config(doc); + requests = new RequestList(config.requests, config); }); it('should load valid requests', () => { - const request = requests.list[0]; - - expect(requests.list.length).toBe(3); - expect(request.VERB).toBe('POST'); - expect(request.ENDPOINT).toBe(endpoint + '/session'); + expect(requests.list.length).toBe(2); }); it('should fetch dependencies', () => { @@ -64,7 +61,7 @@ describe('RequestList', () => { it('should fail if the request fails', async () => { requestPromiseNativeMock.fail = true; - await expect(requests.execByAlias('user')).rejects.toThrow(Error); + await expect(requests.execByAlias('user')).rejects.toThrow(); }); it('should return a cached result if available', async () => { @@ -74,6 +71,18 @@ describe('RequestList', () => { }); it('should fail if the alias is not found', async () => { - await expect(requests.execByAlias('notAnAlias')).rejects.toThrow(Error); + await expect(requests.execByAlias('notAnAlias')).rejects.toThrow(); + }); + + it(`should fail if a given request doesn't have an alias`, () => { + let config = new Config({ + 'GET /hello': { + headers: { + hello: 1 + } + } + }); + + expect(() => new RequestList(config.requests, config)).toThrow(); }); }); diff --git a/src/beau.js b/src/beau.js index 35ead4f..bac1933 100644 --- a/src/beau.js +++ b/src/beau.js @@ -1,54 +1,11 @@ -const deepMerge = require('deepmerge'); - const RequestList = require('./requestList'); -const requestRegex = require('./shared').requestRegex; +const Config = require('./config'); class Beau { constructor(doc) { - this.defaults = { - VERSION: 1, - CACHE: false, - ENDPOINT: '', - PLUGINS: [], - DEFAULTS: [], - ENVIRONMENT: {} - }; + this.config = new Config(doc); - this.configKeys = Object.keys(this.defaults); - this.config = this.loadConfig(doc); - this.requests = this.getRequests(doc); - this.requests = new RequestList(this.requests, this.config); - } - - getRequests(doc) { - let requests = Object.keys(doc).filter(key => { - return requestRegex.test(key); - }); - - let results = {}; - requests.forEach(r => { - if (typeof doc[r] === 'string') { - results[r] = { - ALIAS: doc[r] - }; - } else { - results[r] = doc[r]; - } - - results[r] = deepMerge(this.config.DEFAULTS, results[r]); - }); - - return results; - } - - loadConfig(doc) { - let result = this.defaults; - - Object.keys(doc) - .filter(k => this.configKeys.indexOf(k.toUpperCase()) > -1) - .forEach(k => (result[k.toUpperCase()] = doc[k])); - - return result; + this.requests = new RequestList(this.config.requests, this.config); } } diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..257ad55 --- /dev/null +++ b/src/config.js @@ -0,0 +1,92 @@ +const deepMerge = require('deepmerge'); +const { requestRegex, UpperCaseKeys } = require('./shared'); + +class Config { + constructor(doc) { + this.defaultConfigValues = { + VERSION: 1, + CACHE: false, + ENDPOINT: '', + PLUGINS: [], + DEFAULTS: {}, + ENVIRONMENT: {}, + HOSTS: [] + }; + + this.configKeys = Object.keys(this.defaultConfigValues); + this.doc = doc; + + let config = this.loadConfig(doc); + this.configKeys.forEach(k => { + this[k] = config[k] || this.defaultConfigValues[k]; + }); + + this.requests = []; + + this.loadRequests(doc, { + DEFAULTS: this.DEFAULTS, + ENDPOINT: this.ENDPOINT + }); + + this.loadHosts(this.HOSTS, config); + } + + loadHosts(hosts, rootConfig) { + hosts.forEach(host => { + if (typeof host.host === 'undefined') { + throw new Error(`Host doesn't indicate it's host name.`); + } + + let config = deepMerge( + this.defaultConfigValues, + this.loadConfig(host) + ); + + config.DEFAULTS = deepMerge(rootConfig.DEFAULTS, config.DEFAULTS); + + this.loadRequests(host, { + DEFAULTS: config.DEFAULTS, + ENDPOINT: config.ENDPOINT, + NAMESPACE: host.host + }); + }); + } + + loadRequests(host, settings) { + 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); + + if (settings.NAMESPACE) { + request.ALIAS = `${settings.NAMESPACE}:${request.ALIAS}`; + } + + request.REQUEST = key; + request.ENDPOINT = settings.ENDPOINT; + + let defaults = UpperCaseKeys(settings.DEFAULTS); + + return deepMerge(defaults, request); + }); + + this.requests = this.requests.concat(requests); + } + + loadConfig(host) { + let config = {}; + + Object.keys(host) + .filter(k => this.configKeys.includes(k.toUpperCase())) + .forEach(k => (config[k.toUpperCase()] = host[k])); + + return config; + } +} + +module.exports = Config; diff --git a/src/request.js b/src/request.js index abbb65b..40f57da 100644 --- a/src/request.js +++ b/src/request.js @@ -1,24 +1,30 @@ const request = require('request-promise-native'); -const { httpVerbs, requestRegex, replacementRegex } = require('./shared'); +const { + httpVerbs, + requestRegex, + replacementRegex, + UpperCaseKeys +} = require('./shared'); const RequestList = require('./requestList'); const RequestCache = require('./requestCache'); class Request { constructor(req) { - let config = {}; this.originalRequest = req; - Object.keys(req).forEach(k => (config[k.toUpperCase()] = req[k])); - const { REQUEST, ALIAS, PAYLOAD, ENDPOINT, PARAMS, - HEADERS, - DOCUMENTATION - } = config; + HEADERS + } = UpperCaseKeys(req); + + if (!ALIAS) { + throw new Error(`${REQUEST} is missing an alias.`); + } + const { verb, path } = this.parseRequest(REQUEST); this.VERB = verb; @@ -29,11 +35,6 @@ class Request { this.PARAMS = PARAMS; this.ALIAS = ALIAS; - this.DOCUMENTATION = DOCUMENTATION; - - if (typeof this.ALIAS === 'undefined') { - console.info(`${REQUEST} is missing an alias.`); - } this.DEPENDENCIES = this.findDependencies(req); } diff --git a/src/requestList.js b/src/requestList.js index 8044a6a..17583ed 100644 --- a/src/requestList.js +++ b/src/requestList.js @@ -4,11 +4,12 @@ const httpVerbs = require('./shared').httpVerbs; const requireg = require('requireg'); class RequestList { - constructor(doc = {}, config = {}) { + constructor(requests = [], config = {}) { this.config = config; + this.requests = requests; this.modifiers = this.loadPlugins(); - this.list = this.loadRequests(doc); + this.list = this.loadRequests(); this.cache = new RequestCache(); this.cache.add(`$env`, this.config.ENVIRONMENT); @@ -32,7 +33,9 @@ class RequestList { return this.applyPostResponseModifiers(response); } catch (reason) { throw new Error( - `Request: ${request.VERB} ${request.ENDPOINT} FAILED. \n${reason}` + `Request: ${request.VERB} ${ + request.ENDPOINT + } FAILED. \n${reason}` ); } } @@ -44,19 +47,18 @@ class RequestList { return this.cache; } - loadRequests(doc) { - const requests = Object.keys(doc).filter(key => { - const verb = key.split(' ')[0].toUpperCase(); - return httpVerbs.indexOf(verb) > -1; + 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.map(request => { - doc[request] = doc[request] || {}; - doc[request].ENDPOINT = this.config.ENDPOINT; - doc[request].request = request; - - return new Request(doc[request]); - }); + return requests; } loadPlugins() { diff --git a/src/shared.js b/src/shared.js index 47e8545..ae7a66a 100644 --- a/src/shared.js +++ b/src/shared.js @@ -11,10 +11,17 @@ const httpVerbs = [ ]; const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i'); -const replacementRegex = /\$([a-zA-Z\.\d\-\_\/\\]*)/g; +const replacementRegex = /\$([a-zA-Z\.\d\-\_\/\\\:]*)/g; + +const UpperCaseKeys = function(obj) { + let result = {}; + Object.keys(obj).forEach(k => (result[k.toUpperCase()] = obj[k])); + return result; +}; module.exports = { httpVerbs, requestRegex, - replacementRegex + replacementRegex, + UpperCaseKeys };