├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE-MIT ├── Makefile ├── README.md ├── index.js ├── lib ├── admin.js ├── bailout.js ├── config.js ├── cookieSetter.js ├── error.js ├── index.js ├── registerMocks.js └── validate.js ├── package.json ├── release-notes.md ├── templates ├── cookieSetter.handlebars └── index.handlebars └── test ├── admin.js ├── cookieSetter.js ├── error.js ├── index.js ├── mocks └── ridicule.js └── validate.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage/ 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | /*dotfiles node, mocha*/ 2 | { 3 | "node": true, 4 | "esnext": true, 5 | "browser": false, 6 | "bitwise": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "forin": true, 10 | "immed": false, 11 | "latedef": false, 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonew": false, 16 | "plusplus": false, 17 | "regexp": false, 18 | "undef": true, 19 | "unused": false, 20 | "strict": false, 21 | "trailing": true, 22 | "maxparams": 4, 23 | "asi": false, 24 | "boss": false, 25 | "expr": true, 26 | "laxbreak": true, 27 | "loopfunc": true, 28 | "shadow": true, 29 | "nonstandard": true, 30 | "onevar": false, 31 | "predef": [ 32 | "describe", 33 | "it", 34 | "before", 35 | "after", 36 | "beforeEach", 37 | "afterEach" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 @WalmartLabs 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @NODE_ENV=test ./node_modules/.bin/mocha --recursive --timeout 3000 3 | 4 | # Test coverage with Istanbul 5 | ist: 6 | @NODE_ENV=test ./node_modules/.bin/istanbul cover -- ./node_modules/.bin/_mocha --recursive 7 | 8 | .PHONY: test ist 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ridicule 2 | 3 | hapi plugin providing mocks for restful and querystring based APIs 4 | 5 | *** 6 | # NOTICE: 7 | 8 | ## This repository has been archived and is not supported. 9 | 10 | [](http://unmaintained.tech/) 11 | *** 12 | NOTICE: SUPPORT FOR THIS PROJECT HAS ENDED 13 | 14 | This projected was owned and maintained by Walmart. This project has reached its end of life and Walmart no longer supports this project. 15 | 16 | We will no longer be monitoring the issues for this project or reviewing pull requests. You are free to continue using this project under the license terms or forks of this project at your own risk. This project is no longer subject to Walmart's bug bounty program or other security monitoring. 17 | 18 | 19 | ## Actions you can take 20 | 21 | We recommend you take the following action: 22 | 23 | * Review any configuration files used for build automation and make appropriate updates to remove or replace this project 24 | * Notify other members of your team and/or organization of this change 25 | * Notify your security team to help you evaluate alternative options 26 | 27 | ## Forking and transition of ownership 28 | 29 | For [security reasons](https://www.theregister.co.uk/2018/11/26/npm_repo_bitcoin_stealer/), Walmart does not transfer the ownership of our primary repos on Github or other platforms to other individuals/organizations. Further, we do not transfer ownership of packages for public package management systems. 30 | 31 | If you would like to fork this package and continue development, you should choose a new name for the project and create your own packages, build automation, etc. 32 | 33 | Please review the licensing terms of this project, which continue to be in effect even after decommission.##curved-carousel 34 | 35 | > A simple way to create an infinitely scrollable carousel, with optional curvature. 36 | 37 | 38 | ## setup 39 | 40 | The following options are available for configuration. 41 | 42 | `apiPrefix` 43 | A string or array of strings representing the base of the routes that should 44 | be mocked 45 | *Defaults to `/api`* 46 | 47 | `mocksDir` 48 | Directory containing the mock files. 49 | *Defaults to `__dirname + './mocks'`, where __dirname is this plugin* 50 | 51 | `mocksAdminPath` 52 | A relative URL that the admin page for this plugin can be found. 53 | *Defaults to `/admin/mocks`* 54 | 55 | `mocksAdminServerLabels` 56 | An array of labels used during hapi's plugin registration 57 | *Defaults to `['admin']`* 58 | 59 | `enabled` 60 | Wether or not the mocks are enabled. 61 | *Defaults to `false`* 62 | 63 | `enableForceCookiePage` 64 | A boolean representing wether or not to register the cookie setter page. 65 | This page gives a simple button to turn on or off the `always_ridicule` cookie, 66 | which allows for a client to opt-in to forcing mock responses, regardless of 67 | server settings. 68 | Useful for novice testers or devices where bookmarklet creaton is cumbersome. 69 | *Defaults to `false`* 70 | 71 | `forceCookiePath` 72 | A relative URL that the cookie setter page can be found at. 73 | *Defaults to `/cookie`* 74 | 75 | `forceCookieValue` 76 | A string representing the value of the `always_ridicule` cookie. Allows for servers 77 | to have a publically available opt-in mocking method without having the specific 78 | necessary cookie publically known. `expires` value is 30 minutes from the time 79 | it is set. 80 | *Defaults to `"true"`* 81 | 82 | ## how it works 83 | Inside of your `mocksDir`, create a `ridicule.js` file that exposes an array 84 | hapi routes in it's `module.exports`. 85 | 86 | ```javascript 87 | module.exports = [{ 88 | method: 'GET', 89 | path: '/hello', 90 | handler: function(request, reply) { 91 | 92 | reply('hello world'); 93 | } 94 | },{ 95 | method: 'POST', 96 | path: '/goodbye', 97 | handler: function(request, reply) { 98 | 99 | reply('thanks for all the fish'); 100 | } 101 | }] 102 | ``` 103 | 104 | At the start of the server, all configured routes are registered with a 105 | random 40 character prefix. (e.g. the route `/foo` is mocked out at `/(uuid)/foo`) 106 | 107 | When the mock server is [enabled](#enabling-your-mocks), all incoming requests are 108 | matched against the configured `apiPrefix`. When a match is found, the request is 109 | internally forwarded to the mock route handler. 110 | 111 | You have the ability to bailout on mocking out the request in a handler (useful for 112 | when you are mocking out only a subset of a querystring based system, since hapi 113 | matches on paths only). Simply `require` rididcule in the file, and then call 114 | `ridicule.bailout(req, res)`, where `req` and `res` are the request and response 115 | objects inside of the handler. 116 | 117 | ## configuring your mocks 118 | 119 | The most common way of mocking out requests is just to return the data of a json 120 | file within the `reply()` interface of your route handler. 121 | 122 | ``` javascript 123 | var data = require('./mockFile.json'); 124 | 125 | var route = { 126 | method: '*', 127 | path: '/mockPath', 128 | config: { 129 | handler: function (request, reply) { 130 | return reply(data); 131 | } 132 | } 133 | }; 134 | ``` 135 | 136 | However, you have the ability to add additional metadata to the response, such as 137 | custom headers and HTTP status codes. 138 | You just need to wrap up your existing json file inside of another object, 139 | setting the original to `payload`, and add a `ridiculeSettings` field. 140 | 141 | so this request 142 | 143 | ```json 144 | { 145 | "foo": "bar" 146 | } 147 | ``` 148 | 149 | becomes 150 | ```json 151 | { 152 | "ridiculeSettings": { 153 | "statusCode": 418, 154 | "headers": { "Content-Type": "x-stream" } 155 | } 156 | payload: { 157 | "foo": "bar" 158 | } 159 | } 160 | ``` 161 | 162 | Worth noting that since this is added at the very end of the hapi's request flow, 163 | As a result, since `payload` has to be inlined in the mock file, and is therefore 164 | limited to only support string and JSON objects, and not streams or buffers. 165 | 166 | ## enabling your mocks 167 | 168 | There are three ways to enable/disable mocks. 169 | 170 | 1. **The hapi configuration setting** 171 | 172 | You can change the `enabled` opton in your [setup](#setup) to `true`. This enables 173 | it at start, but can changed in the future by.. 174 | 175 | 2. **Toggling the admin page button** 176 | 177 | You can turn mocks on or off at any time by visting the path [configured](#setup) under 178 | `mocksAdminPath`. By default this is `/admin/mocks`. Note that this is 179 | registered behind a hapi label (configurable in your setup, but `['admin']` 180 | by default). This allows you to ship ridicule in your production site, and 181 | have the toggle switch exist on a port that isn't publicly accessible. 182 | 183 | 3. **A cookie on the client** 184 | 185 | If the cookie `always_ridicule` is set with the value from the configured 186 | `forceCookieValue` (default value is the string "true", e.g. `always_ridicule="true"`) 187 | on the client, then the configured mocks will always be served (until it is 188 | deleted, of course). In, addition, `always_ridicule=false` will force the 189 | client to never be mocked out, regardless of the server's current settings. 190 | 191 | ## querystrings 192 | 193 | Since hapi matches routes only on paths (and not any querystring parameters), 194 | you need to be able to match more than one query against a path. For that, 195 | ridicule provides `ridicule.validate` method. 196 | 197 | ### validation 198 | 199 | `ridicule.validate` is a function that takes a request and reply pair, then a validation 200 | object, consisting a `validator` object, and a `callback` function. 201 | 202 | The `validator` object is a plain object, where each key represents the key of 203 | the querystring, and the value is either a string, RegExp, or function that 204 | matches the value of the corresponding value in the querystring. 205 | 206 | For example, the following querystring 207 | 208 | `?category=Music&genre=ska&year=2012` 209 | 210 | would match the following validation 211 | 212 | ```javascript 213 | ... 214 | handler: function(request, reply) { 215 | ridicule.validate(request, reply, { 216 | validator: { 217 | category: 'Music', 218 | genre: function(value) { 219 | return value === 'ska' || value === 'rocksteady' 220 | }, 221 | year: /^\d{4}$/ 222 | }, 223 | callback: function(request, reply) { 224 | reply({'all': 'good'}); 225 | } 226 | }) 227 | } 228 | ... 229 | ``` 230 | 231 | of course you can easily chain multiple checks 232 | 233 | ```javascript 234 | var queriesToCheck = [{ 235 | validator: { 236 | category: 'Music', 237 | genre:'ska', 238 | year: /^\d{4}$/ 239 | }, 240 | callback: function(request, reply) { 241 | reply().file('./authMock.json'); 242 | } 243 | },{ 244 | validator: { 245 | foo: 'bar', 246 | baz: 'biz' 247 | }, 248 | callback: function(request, reply) { 249 | reply({'wordsAre': 'hard'}); 250 | } 251 | }]; 252 | 253 | queriesToCheck.some(function(route) { 254 | return ridicule.validate(request, reply, route); 255 | }); 256 | ``` 257 | 258 | ### bailout 259 | 260 | You may run into an issue where you want to mock out a subset of your requests at 261 | an endpoint, but not all of them. In this case, ridicule provides a handy helper 262 | function, `ridicule.bailout`. 263 | 264 | all you need to do is add it to the end of your query checks, passing through 265 | the handlers `request` and `reply` interfaces. 266 | 267 | ```javascript 268 | var matched = queriesToCheck.some(function(route) { 269 | return ridicule.validate(request, reply, route); 270 | }); 271 | 272 | if (!matched) { 273 | ridicule.bailout(request, reply); 274 | } 275 | ``` 276 | 277 | An alternative to this is setting the validator object on your final query to 278 | `true`. This will automatically match the supplied query, and can act as a catchall 279 | 280 | ```javascript 281 | var queriesToCheck = [{ 282 | validator: { 283 | category: 'Music', 284 | genre:'ska', 285 | year: /^\d{4}$/ 286 | }, 287 | callback: function(request, reply) { 288 | reply().file('./authMock.json'); 289 | } 290 | },{ 291 | validator: true, 292 | callback: function(request, reply) { 293 | reply().file('./catchall.json'); 294 | } 295 | }]; 296 | 297 | queriesToCheck.some(function(route) { 298 | return ridicule.validate(request, reply, route); 299 | }); 300 | ``` 301 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib'); 2 | -------------------------------------------------------------------------------- /lib/admin.js: -------------------------------------------------------------------------------- 1 | var Config = require('./config'); 2 | 3 | module.exports = { 4 | init: function (plugin) { 5 | 6 | plugin.select(Config.get('mocksAdminServerLabels')).route([ 7 | { 8 | 'method': 'GET', 9 | 'path': Config.get('mocksAdminPath'), 10 | 'handler': function (request, reply) { 11 | 12 | reply.view('index', Config.get()); 13 | } 14 | }, 15 | { 16 | 'method': 'POST', 17 | 'path': Config.get('mocksAdminPath'), 18 | 'handler': function (request, reply) { 19 | 20 | Config.set('enabled', request.payload.enabled === 'true'); 21 | reply().redirect(Config.get('mocksAdminPath')); 22 | } 23 | } 24 | ]); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/bailout.js: -------------------------------------------------------------------------------- 1 | var _each = require('lodash.foreach'), 2 | Config = require('./config'); 3 | 4 | module.exports = function (request, reply) { 5 | 6 | var deregistered = request.url.href.match(new RegExp(Config.get('mocksRouteBase') + '(.*)')); 7 | 8 | // Don't accept encoding so we don't have to gunzip stuff later on 9 | delete request.headers['accept-encoding']; 10 | 11 | delete request.app.ridiculed; 12 | 13 | request.headers['x-dontridicule'] = 'true'; 14 | 15 | request.server.inject({ 16 | method: request.method, 17 | url: deregistered[1], 18 | headers: request.headers, 19 | payload: request.payload, 20 | credentials: request.auth.credentials, 21 | ridiculed: true 22 | }, function(res) { 23 | 24 | var headers = res.headers; 25 | var response = reply(res.result); 26 | 27 | // pass along upstream status code 28 | response.code(res.statusCode); 29 | 30 | // include all upstream headers 31 | _each(headers, function(value, name) { 32 | 33 | response.header(name, value); 34 | }); 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | var Crypto = require('crypto'), 2 | Joi = require('joi'), 3 | LogErr = require('./error'), 4 | stringOrArrayOfStrings = Joi.alternatives().try(Joi.array().min(1).includes(Joi.string()), Joi.string()), 5 | internals = {}; 6 | 7 | 8 | internals.schema = Joi.object().keys({ 9 | 'apiPrefix': stringOrArrayOfStrings.default('/api'), 10 | 'mocksDir': Joi.string().min(1).default(__dirname + '/../mocks'), 11 | 'mocksAdminPath': Joi.string().min(1).default('/admin/mocks'), 12 | 'mocksAdminServerLabels': stringOrArrayOfStrings.default(['admin']), 13 | 'mocksRouteBase': Joi.string().default('/' + Crypto.randomBytes(20).toString('hex')), 14 | 'enabled': Joi.boolean().default(false), 15 | 'enableForceCookiePage': Joi.boolean().default(false), 16 | 'forceCookiePath': Joi.string().default('/cookie'), 17 | 'forceCookieValue': Joi.string().default('true') 18 | }); 19 | 20 | 21 | internals.Config = function (config) { 22 | 23 | var self = this; 24 | 25 | if (config.mocksRouteBase) { 26 | LogErr('mocksRouteBase is an internal only property. ignoring supplied value: ' + config.mocksRouteBase); 27 | delete config.mocksRouteBase; 28 | } 29 | 30 | Joi.validate(config, internals.schema, function(err, validatedConfig) { 31 | if (err) { 32 | throw new Error(err); 33 | } 34 | self.config = validatedConfig; 35 | }); 36 | }; 37 | 38 | 39 | internals.Config.prototype.get = function (key) { 40 | 41 | // Return the entire config when called without a key 42 | return key ? this.config[key] : this.config; 43 | }; 44 | 45 | 46 | internals.Config.prototype.set = function (key, value) { 47 | 48 | this.config[key] = value; 49 | return this.config[key]; 50 | }; 51 | 52 | 53 | exports.init = function (config) { 54 | 55 | internals.config = new internals.Config(config); 56 | exports.get = internals.config.get.bind(internals.config); 57 | exports.set = internals.config.set.bind(internals.config); 58 | }; 59 | -------------------------------------------------------------------------------- /lib/cookieSetter.js: -------------------------------------------------------------------------------- 1 | var Config = require('./config'); 2 | 3 | module.exports = { 4 | init: function (plugin) { 5 | 6 | plugin.route({ 7 | 'method': 'GET', 8 | 'path': '/cookie', 9 | 'handler': function (request, reply) { 10 | 11 | reply.view('cookieSetter', Config.get()); 12 | } 13 | }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /lib/error.js: -------------------------------------------------------------------------------- 1 | var SupportsColor = require('supports-color'), 2 | startRed = SupportsColor ? '\u001b[31m' : '', 3 | endRed = SupportsColor ? '\u001b[39m' : ''; 4 | 5 | module.exports = function(str) { 6 | return console.error(startRed + 'ridicule: ' + str + endRed); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var Cookie = require('cookie'), 2 | Handlebars = require('handlebars'), 3 | Path = require('path'), 4 | _each = require('lodash.foreach'), 5 | Admin = require('./admin'), 6 | Bailout = require('./bailout'), 7 | Config = require('./config'), 8 | CookieSetter = require('./cookieSetter'), 9 | RegisterMocks = require('./registerMocks'), 10 | Validate = require('./validate'), 11 | EscapeRegex = require('escape-regexp'), 12 | apiCheck; 13 | 14 | exports.register = function (plugin, options, next) { 15 | 16 | var apiPrefix; 17 | 18 | plugin.views({ 19 | 'engines': { 20 | 'handlebars': { 21 | 'module': Handlebars 22 | } 23 | }, 24 | 'path': __dirname + '/../templates' 25 | }); 26 | 27 | Config.init(options); 28 | 29 | // Setup the admin page 30 | Admin.init(plugin); 31 | 32 | if (Config.get('enableForceCookiePage')) { 33 | CookieSetter.init(plugin); 34 | } 35 | 36 | // Concated wtth an empty array so we can handle strings as well as other arrays 37 | apiPrefix = [].concat(Config.get('apiPrefix')); 38 | 39 | Config.set('mocksDir', Path.resolve(Config.get('mocksDir'))); 40 | 41 | Config.set('apiPrefix', apiPrefix.map(EscapeRegex).join('|')); 42 | 43 | // Regex used to determine if a request should be mocked 44 | apiCheck = new RegExp('^(' + Config.get('apiPrefix') + ')(.*)'); 45 | 46 | // Register the onRequest prehandler 47 | plugin.ext('onRequest', onRequest); 48 | plugin.ext('onPreResponse', onPreResponse); 49 | 50 | // Wire up the actual mocks 51 | plugin.route(RegisterMocks(Config)); 52 | 53 | next(); 54 | }; 55 | 56 | exports.register.attributes = { 57 | 58 | pkg: require('../package.json') 59 | }; 60 | 61 | exports.bailout = Bailout; 62 | 63 | exports.validate = Validate; 64 | 65 | // Switching logic - determines wether or not to mock out a URL, or serve it as normal 66 | var onRequest = function (req, next) { 67 | // Cookies are normally accessed from `req.state`, but they're not set at this 68 | // point in the request cycle 69 | var cookies = Cookie.parse(req.raw.req.headers.cookie || ''); 70 | var enable = Config.get('enabled') || cookies.always_ridicule === Config.get('forceCookieValue'); 71 | var disabled = cookies.always_ridicule === 'false'; 72 | var route; 73 | 74 | if (enable && !disabled) { 75 | route = req.url.path.match(apiCheck); 76 | 77 | if (route && !('x-dontridicule' in req.headers)) { 78 | req.app.ridiculed = true; 79 | req.setUrl(Path.normalize(Config.get('mocksRouteBase') + '/' + route[0])); 80 | } else { 81 | delete req.headers['x-dontridicule']; 82 | } 83 | 84 | } 85 | 86 | next(); 87 | }; 88 | 89 | 90 | // Optionally modify the data returned from the mocked response 91 | var onPreResponse = function (req, next) { 92 | 93 | if (req.app.ridiculed) { 94 | var data = req.response.source; 95 | var settings = data && data.ridiculeSettings; 96 | 97 | if (settings) { 98 | req.response.statusCode = settings.statusCode || 200; 99 | req.response.source = data.payload; 100 | 101 | if (typeof data.payload === 'string') { 102 | req.response.settings.stringify = null; 103 | req.response.type('text/html'); 104 | } 105 | 106 | _each(settings.headers, function (value, name) { 107 | 108 | req.response.header(name, value); 109 | }); 110 | } 111 | } 112 | 113 | next(); 114 | }; 115 | -------------------------------------------------------------------------------- /lib/registerMocks.js: -------------------------------------------------------------------------------- 1 | var _defaults = require('lodash.defaults'), 2 | Fs = require('fs'), 3 | LogErr = require('./error'), 4 | Path = require('path'); 5 | 6 | module.exports = function (Config) { 7 | 8 | var ridiculeFile = Path.resolve(Config.get('mocksDir')) + '/ridicule.js', 9 | registeredRoutes; 10 | 11 | if (Fs.existsSync(ridiculeFile)) { 12 | registeredRoutes = require(ridiculeFile); 13 | } else { 14 | LogErr(ridiculeFile + ' does not exist, no mocks registered'); 15 | registeredRoutes = []; 16 | } 17 | 18 | return registeredRoutes.map(function (route) { 19 | 20 | return _defaults({ 21 | path: Config.get('mocksRouteBase') + route.path 22 | }, route); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/validate.js: -------------------------------------------------------------------------------- 1 | module.exports = function (request, reply, validation) { 2 | var _isFunction = require('lodash.isfunction'); 3 | var validator = validation.validator || {}; 4 | var validated; 5 | 6 | // Allow for catchall validation 7 | if (validator === true) { 8 | validated = true; 9 | } 10 | 11 | if (!validated) { 12 | var requestQuery = request.query || {}; 13 | validated = Object.keys(requestQuery).every(function (prop) { 14 | 15 | var validateValue = validator[prop]; 16 | var requestQueryValue = requestQuery[prop]; 17 | 18 | // Fail if there is no validation for this field 19 | if (validateValue === undefined) { 20 | return; 21 | } 22 | 23 | // Accept any and everything 24 | if (validateValue === true) { 25 | return true; 26 | } 27 | 28 | // Validate against a literal Regex 29 | if (validateValue.exec) { 30 | return !!validateValue.exec(requestQueryValue); 31 | } 32 | 33 | if (_isFunction(validateValue)) { 34 | return !!validateValue(requestQueryValue); 35 | } 36 | 37 | return validateValue === requestQueryValue; 38 | 39 | }); 40 | } 41 | 42 | if (validated) { 43 | if (validation.callback) { 44 | process.nextTick(function() { 45 | 46 | validation.callback(request, reply); 47 | }); 48 | } 49 | 50 | return true; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ridicule", 3 | "version": "1.0.0", 4 | "main": "index", 5 | "engines": { 6 | "node": "0.10.x" 7 | }, 8 | "dependencies": { 9 | "cookie": "^0.1.2", 10 | "escape-regexp": "0.0.1", 11 | "handlebars": "2.x.x", 12 | "joi": "^4.6.1", 13 | "lodash.defaults": "^2.4.1", 14 | "lodash.foreach": "^2.4.1", 15 | "lodash.isfunction": "^2.4.1", 16 | "supports-color": "^0.2.0" 17 | }, 18 | "keywords": [ 19 | "hapi", 20 | "plugin", 21 | "mocks" 22 | ], 23 | "devDependencies": { 24 | "chai": "^1.9.1", 25 | "hapi": "^5.1.0", 26 | "istanbul": "^0.2.13", 27 | "mocha": "^1.20.1", 28 | "sinon": "^1.10.2" 29 | }, 30 | "scripts": { 31 | "test": "make ist" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /release-notes.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## Development 4 | 5 | [Commits](https://github.com/walmartlabs/ridicule/compare/v1.0.0...master) 6 | 7 | ## v1.0.0 - July 2nd, 2014 8 | - [#5](https://github.com/walmartlabs/ridicule/pull/5) - Convert mock admin to use post/redirect/get loop ([@kpdecker](https://api.github.com/users/kpdecker)) 9 | - [#6](https://github.com/walmartlabs/ridicule/pull/6) - Refactor validator helper ([@kpdecker](https://api.github.com/users/kpdecker)) 10 | - [#7](https://github.com/walmartlabs/ridicule/pull/7) - Add remaining test coverage ([@kpdecker](https://api.github.com/users/kpdecker)) 11 | - stylistic modifications - 67d971c 12 | - Include jshint file - 026bbe5 13 | - Add test runner - 9b667b9 14 | 15 | Compatibility notes: 16 | `ridicule.validate` now takes a request and reply pair, rather than just the query string 17 | 18 | [Commits](https://github.com/walmartlabs/ridicule/compare/v0.1.0...v1.0.0) 19 | 20 | ## v0.1.0 - July 1st, 2014 21 | - [#4](https://github.com/walmartlabs/ridicule/pull/4) - add client side cookie switch to force mock enabling ([@patrickkettner](https://api.github.com/users/patrickkettner)) 22 | - expose register.attribuets for hapi 6.x+ support - af6e0a7 23 | - remove unused module - 1ac73f3 24 | 25 | [Commits](https://github.com/walmartlabs/ridicule/compare/v0.0.2...v0.1.0) 26 | 27 | ## v0.0.2 - June 17th, 2014 28 | - switch hapi.utils out for hoek - 275cfdf 29 | 30 | [Commits](https://github.com/walmartlabs/ridicule/compare/b450fd1...v0.0.2) 31 | -------------------------------------------------------------------------------- /templates/cookieSetter.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |