From 48383bee247eab676299a46d1ef716e9e6e6c4aa Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Mon, 6 Aug 2018 03:22:30 -0400 Subject: [PATCH 1/2] feat(delay response): add ability to delay response time * added delay flag, in milliseconds * open check for delay and forceStatus params * generic console.info JSON to "response" copy * lowercased options to avoid being needlessly specific * readme examples --- README.md | 32 +++++++++++++++++++++++++++++ src/api_mock.js | 8 ++++++-- src/index.js | 53 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6f39433..59a8da7 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,13 @@ From there you should be able to navigate to ### More examples, and custom responses +Apidoc Mock adds in a few different custom flags to help you identify or demonstrate API responses +- @apiMock {Random|RandomResponse} - pull a random response from either success or error examples +- @apiMock {RandomSuccess} - pull a random success from success examples +- @apiMock {RandomError} - pull a random error from error examples +- @apiMock {ForceStatus} [HTTP STATUS] - force a specific http status +- @apiMock {DelayResponse} [MILLISECONDS] - force (in milliseconds) a delayed response + 1. Get random responses from both `success` and `error` examples with the `@apiMock {RandomResponse}` annotation ```js /** @@ -176,3 +183,28 @@ From there you should be able to navigate to */ const getExample = () => {}; ``` + +1. Delay a response status with the `@apiMock {DelayResponse} [MILLISECONDS GO HERE]` annotation. + ```js + /** + * @api {get} /hello/world/ + * @apiMock {DelayResponse} 3000 + * @apiSuccess {String} foo + * @apiSuccess {String} bar + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * { + * "foo": "hello", + * "bar": "world", + * } + * @apiError {String} bad + * @apiError {String} request + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 400 OK + * { + * "bad": "hello", + * "request": "world", + * } + */ + const getExample = () => {}; + ``` diff --git a/src/api_mock.js b/src/api_mock.js index d5f8b34..92fa237 100644 --- a/src/api_mock.js +++ b/src/api_mock.js @@ -1,3 +1,7 @@ +/** + * Configure Apidoc output. Filter custom "apiMock" related key/value + * pairs such as randomResponse, forceStatus, or delayResponse. + */ let group = ''; const parse = (content, source, defaultGroup) => { @@ -8,7 +12,7 @@ const parse = (content, source, defaultGroup) => { let key = (keyValue[0] || '').replace(/({|^\s+|\s+$)/, ''); const value = (keyValue[1] || '').replace(/(^\s+|\s+$)/, ''); - key = key.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) { + key = key.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => { return index === 0 ? letter.toLowerCase() : letter.toUpperCase(); }); @@ -22,6 +26,6 @@ const path = () => `local.mock.${getGroup()}`; module.exports = { parse, path, - getGroup: getGroup, + getGroup, method: 'push' }; diff --git a/src/index.js b/src/index.js index 09d921b..86ff46a 100644 --- a/src/index.js +++ b/src/index.js @@ -78,24 +78,41 @@ class LoadApi { if (mockSettings.mock && mockSettings.mock.settings && mockSettings.mock.settings.length) { mockSettings.mock.settings.forEach(val => { const keys = Object.keys(val || {}); - const key = keys[0] || null; + const key = keys[0] || ''; - switch (key) { - case 'forceStatus': + switch (key.toLowerCase()) { + case 'delay': + case 'delayresponse': + settings.delay = parseInt(val[key], 10); + + if (Number.isNaN(settings.delay)) { + settings.delay = 1000; + } + + break; + case 'force': + case 'forcestatus': + case 'forcedstatus': settings.forceStatus = parseInt(val[key], 10); + + if (Number.isNaN(settings.forceStatus)) { + settings.forceStatus = 200; + } + break; case 'response': settings.response = 'response'; break; - case 'randomResponse': + case 'random': + case 'randomresponse': settings.response = 'response'; settings.reload = true; break; - case 'randomSuccess': + case 'randomsuccess': settings.response = 'success'; settings.reload = true; break; - case 'randomError': + case 'randomerror': settings.response = 'error'; settings.reload = true; break; @@ -175,7 +192,7 @@ class LoadApi { return; } - console.info('ApiDoc finished...\tloading JSON'); + console.info('ApiDoc finished...\tloading response'); return JSON.parse(fs.readFileSync(apiJsonFile, 'utf8')); } @@ -367,7 +384,16 @@ class LoadApi { response.append('WWW-Authenticate', 'Spoof response'); response.status(401); response.set('Content-Type', authObj.type); - response.end(authObj.content || 'Authorization Required'); + + if (mockSettings.delay > 0) { + setTimeout( + () => response.end(authObj.content || 'Authorization Required'), + mockSettings.delay + ); + } else { + response.end(authObj.content || 'Authorization Required'); + } + return; } } @@ -379,7 +405,12 @@ class LoadApi { response.set('Content-Type', type); response.status(httpStatus); - response.send(content); + + if (mockSettings.delay > 0) { + setTimeout(() => response.send(content), mockSettings.delay); + } else { + response.send(content); + } }); routesLoaded += 1; @@ -390,7 +421,9 @@ class LoadApi { if (routesLoaded) { this.app.listen(port, () => - console.info(`JSON finished...\tloaded routes\nMock finished...\tforwarded port ${port}`) + console.info( + `Response finished...\tloaded routes\nMock finished...\tforwarded port ${port}` + ) ); } else { console.info(`Mock waiting...`); From 9e008bce6a1fe0f3af638be8d36f0d7eec08dfab Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Sun, 12 Aug 2018 17:29:10 -0400 Subject: [PATCH 2/2] perf(nodeJs): upgraded nodeJs and packages * NodeJs upgraded, going with breaking release * minor package updates --- package.json | 8 ++++---- yarn.lock | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 5ea664c..0816972 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "apidoc-mock", - "version": "1.0.2", + "version": "2.0.0", "description": "Creates a mock server from apiDoc comments.", "author": "cdcabrera", "private": true, @@ -13,7 +13,7 @@ "url": "https://github.com/cdcabrera/apidoc-mock/issues" }, "engines": { - "node": ">=8.9.1" + "node": ">=8.11.3" }, "main": "src/index.js", "scripts": { @@ -38,7 +38,7 @@ }, "devDependencies": { "babel-eslint": "^8.2.6", - "eslint": "^5.2.0", + "eslint": "^5.3.0", "eslint-config-esnext": "^3.0.0", "eslint-config-node": "^3.0.0", "eslint-config-prettier": "^2.9.0", @@ -46,6 +46,6 @@ "eslint-plugin-import": "^2.13.0", "eslint-plugin-node": "^7.0.1", "eslint-plugin-prettier": "^2.6.2", - "prettier": "^1.13.7" + "prettier": "^1.14.2" } } diff --git a/yarn.lock b/yarn.lock index 6737a22..a25a428 100644 --- a/yarn.lock +++ b/yarn.lock @@ -922,9 +922,9 @@ eslint@^4.19.1: table "4.0.2" text-table "~0.2.0" -eslint@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.2.0.tgz#3901ae249195d473e633c4acbc370068b1c964dc" +eslint@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.3.0.tgz#53695aca5213968aacdf970ccb231e42a2b285f8" dependencies: ajv "^6.5.0" babel-code-frame "^6.26.0" @@ -957,7 +957,7 @@ eslint@^5.2.0: path-is-inside "^1.0.2" pluralize "^7.0.0" progress "^2.0.0" - regexpp "^1.1.0" + regexpp "^2.0.0" require-uncached "^1.0.3" semver "^5.5.0" string.prototype.matchall "^2.0.0" @@ -2327,9 +2327,9 @@ prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" -prettier@^1.13.7: - version "1.13.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281" +prettier@^1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.2.tgz#0ac1c6e1a90baa22a62925f41963c841983282f9" process-nextick-args@~2.0.0: version "2.0.0" @@ -2441,7 +2441,7 @@ regexp.prototype.flags@^1.2.0: dependencies: define-properties "^1.1.2" -regexpp@^1.0.1, regexpp@^1.1.0: +regexpp@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab"