Skip to content
This repository has been archived by the owner on Jan 5, 2023. It is now read-only.

Commit

Permalink
Merge pull request #5 from jimmyeisenhauer/dynamic-data
Browse files Browse the repository at this point in the history
1.1.0 release
  • Loading branch information
jimmyeisenhauer authored Jan 30, 2017
2 parents 1b98baf + 322991b commit 09354f7
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 11 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@ The changelog for BOKOR includes information about the each release including an


---
## 1.0.1
## 1.0.1

### Release Notes
#### Added

- I AM BORN!


## 1.1.0

### Release Notes
#### Added

- Relative Dates Time objects! Ability to have dynamic mocked date time values.
- Ability to turn off the admin server via configuration.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@

Bokor is a simple, Record and Playback Mock Server written in Node.js, utilized for Service Virtualization.

Bokor is very similar to the many VCR-like tools out there today, but prides itself on its ease of use and speed to setup. It can be utilized for any mocking needs, but was primarily developed for mocking back end service calls in automated UI testing.

Bokor was developed and is in use at Nike since early 2016. There, it is used to improve the speed, reliability and test coverage of the integration and user interface test suites for native mobile applications.

- [Installation](#installation)
- [Usage](#usage)
- [Server Configuration](#server-configuration)
- [Filter Configuration](#filter-configuration)
- [Advanced Configuration](#advanced-configuration)
- [Relative Date Time Objects](#relative-date-time-objects)
- [Static Resources](#static-resources)
- [Admin Server](#admin-server)
- [Port](#port)
- [Data Fixtures](#data-fixtures)
- [Data Bins](#data-bins)
- [Fixture Filenames](#fixture-filenames)
Expand Down Expand Up @@ -125,6 +133,43 @@ bokor server rolled lucky 7777

### Advanced Configuration

#### Relative Date Time Objects
Often we find the need to have date time results relative from the current date time. For example; "5 minutes from now" or "Exactly 3 days from now". With Bokor you can manipulate your recorded date time values in any possible way you can imagine.

##### Create `datetimes.properties` file
First create a key name that is meaningful like: `NOW_DATETIME_UTC`. Next utilizing the [Moment.JS library](http://momentjs.com/) create your javascript function that will manipulate the date time to your needs like: `moment().utc().format()`, making sure to escape any single quotes.

```javascript
var datetimes = {
datetime1: {
key: 'NOW_DATETIME_UTC',
value: 'moment().utc().format()',
},
datetime2: {
key: 'NOW_DATETIME_UTC_PLUS_10_MINUTES',
value: 'moment().utc().add(10, \'m\').format()',
}
};
module.exports = datetimes;
```

##### Add the date time configuration to the `server.js` file.
```javascript
var serversProperties = require('./servers.properties');
var filtersProperties = require('./filters.properties');
var datetimesProperties = require('./datetimes.properties');

var bokor = require('bokor');

bokor.start({
servers : serversProperties,
filters : filtersProperties,
datetimes : datetimesProperties
});
```
Once you have completed the configuration modify your recorded data fixtures, by replacing date time values with a the key values you created. Bokor will dynamically create date time values the next time you request that data fixture.


#### Static Resources
By default Bokor serves any static resource in the `static_files` folder. You can modify this folder name by adjusting the server config.
```javascript
Expand All @@ -135,6 +180,16 @@ staticFileLocation: customFolder
});
```

#### Admin Server
By default Bokor runs an admin server on port 58080. If you do not need this feature you can turn off the admin server by adjusting the server config.
```javascript
bokor.start({
servers : serversProperties,
filters : filtersProperties,
admin: false
});
```

#### Port
By default Bokor runs on port 7777. You can modify this port by adjusting the server config.
```javascript
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"login": "jimmyeisenhauer",
"id": 791447,
"avatar_url": "https://avatars.githubusercontent.com/u/791447?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/jimmyeisenhauer",
"html_url": "https://github.com/jimmyeisenhauer",
"followers_url": "https://api.github.com/users/jimmyeisenhauer/followers",
"following_url": "https://api.github.com/users/jimmyeisenhauer/following{/other_user}",
"gists_url": "https://api.github.com/users/jimmyeisenhauer/gists{/gist_id}",
"starred_url": "https://api.github.com/users/jimmyeisenhauer/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/jimmyeisenhauer/subscriptions",
"organizations_url": "https://api.github.com/users/jimmyeisenhauer/orgs",
"repos_url": "https://api.github.com/users/jimmyeisenhauer/repos",
"events_url": "https://api.github.com/users/jimmyeisenhauer/events{/privacy}",
"received_events_url": "https://api.github.com/users/jimmyeisenhauer/received_events",
"type": "User",
"site_admin": false,
"name": "Jimmy Eisenhauer",
"company": "@Nike-Inc",
"blog": "http://www.twitter.com/jimmyeisenhauer",
"location": "Beaverton, Oregon",
"email": null,
"hireable": null,
"bio": "Software Engineer ",
"public_repos": 42,
"public_gists": 8,
"followers": 4,
"following": 14,
"created_at": "NOW_DATETIME_UTC",
"updated_at": "NOW_DATETIME_UTC_PLUS_10_MINUTES"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"statusCode": 200,
"headers": {
"server": "GitHub.com",
"date": "Sat, 28 Jan 2017 23:29:11 GMT",
"content-type": "application/json; charset=utf-8",
"status": "200 OK",
"x-ratelimit-limit": "60",
"x-ratelimit-remaining": "57",
"x-ratelimit-reset": "1485646272",
"cache-control": "public, max-age=60, s-maxage=60",
"vary": "Accept, Accept-Encoding",
"etag": "\"7c0f10ed5c54c3f458b1b2e8809d9c80\"",
"last-modified": "Tue, 24 Jan 2017 09:46:25 GMT",
"x-github-media-type": "github.v3; format=json",
"access-control-expose-headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval",
"access-control-allow-origin": "*",
"content-security-policy": "default-src 'none'",
"strict-transport-security": "max-age=31536000; includeSubdomains; preload",
"x-content-type-options": "nosniff",
"x-frame-options": "deny",
"x-xss-protection": "1; mode=block",
"x-served-by": "8dd185e423974a7e13abbbe6e060031e",
"x-github-request-id": "C542:03EA:766947E:9B03009:588D2946"
},
"url": "https://api.github.com:443/users/jimmyeisenhauer",
"time": 697,
"request": {
"method": "GET",
"headers": {
"accept": "*/*",
"user-agent": "curl/7.51.0",
"host": "api.github.com"
}
}
}
11 changes: 11 additions & 0 deletions examples/source_example/datetimes.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
var datetimes = {
datetime1: {
key: 'NOW_DATETIME_UTC',
value: 'moment().utc().format()',
},
datetime2: {
key: 'NOW_DATETIME_UTC_PLUS_10_MINUTES',
value: 'moment().utc().add(10, \'m\').format()',
}
};
module.exports = datetimes;
4 changes: 3 additions & 1 deletion examples/source_example/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@

var serversProperties = require('./servers.properties');
var filtersProperties = require('./filters.properties');
var datetimesProperties = require('./datetimes.properties');

var bokor = require('../../');

bokor.start({
servers : serversProperties,
filters : filtersProperties
filters : filtersProperties,
datetimes : datetimesProperties
});
20 changes: 19 additions & 1 deletion lib/bokor.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
var express = require('express');
var httpProxy = require('http-proxy');
var https = require('https');
var moment = require('moment'); // jshint ignore:line
var bokorOptions = {};
require('colors');
require('http');
Expand Down Expand Up @@ -42,7 +43,14 @@ function start(options) {
bokorOptions.staticFileLocation = options.staticFileLocation || 'static_files'; // bokor server static file location

// -- sepia config --------------
var sepia = require('./sepia').withSepiaServer();
// default bokor admin server on by default
var sepia;
if (options.admin === false) {
sepia = require('./sepia');
} else {
sepia = require('./sepia').withSepiaServer();
}

sepia.configure({
verbose: true,
debug: false,
Expand All @@ -57,6 +65,16 @@ function start(options) {
});


// setup the dynamic datetimes for sepia
if (options.datetimes != null) {
bokorOptions.datetimes = options.datetimes;
var datetimeConfigs = Object.keys(bokorOptions.datetimes);
datetimeConfigs.forEach(datetime => {
var datetimeConfig = options.datetimes[datetime];
sepia.substitute(datetimeConfig.key, function () { return eval(datetimeConfig.value) ;}); // jshint ignore:line
});
}

// setup the url filters for sepia
var filterConfigs = Object.keys(bokorOptions.filters);
filterConfigs.forEach(filterName => {
Expand Down
11 changes: 10 additions & 1 deletion lib/sepia/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,16 @@ Examples of this functionality can be seen in `examples/headers.js`:
rm -r fixtures # in case you had previously generated fixtures
VCR_MODE=cache node examples/headers

## Hiding Sensitive Data

It is good practice to avoid checking sensitive data into source control. Sepia
can substituting specific text in headers and bodies with values you specify. The substitute function takes a substitution string as a first argument and a function which
returns the actual value, presumably retrieved from the environment. Your fixtures
will contain the substitution string, and can be safely committed to source control.

var sepia = require('sepia');
sepia.substitute('<SUBSTITUTION1>', function() { return process.env.MY_API_SECRET; });

## Languages

A downstream request may return different data based on the language requested
Expand Down Expand Up @@ -407,4 +417,3 @@ data is retrieved from a file and sent back using a dummy response object.
* [Deepank Gupta](https://github.com/deepankgupta)
* [Priyanka Salvi](https://github.com/salvipriyanka/)
* [Ashima Atul](https://github.com/ashimaatul)

1 change: 1 addition & 0 deletions lib/sepia/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ function shutdown(next) {

var sepiaUtil = require('./src/util');
module.exports.filter = sepiaUtil.addFilter;
module.exports.substitute = sepiaUtil.addSubstitution;
module.exports.fixtureDir = sepiaUtil.setFixtureDir;
module.exports.configure = sepiaUtil.configure;
module.exports.withSepiaServer = withSepiaServer;
Expand Down
8 changes: 4 additions & 4 deletions lib/sepia/src/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ module.exports.configure = function(mode) {
// exists, or we're playing back passed in data.
function playback(resHeaders, resBody) {
if (!forceLive) {
var headerContent = fs.readFileSync(filename + '.headers');
var headerContent = sepiaUtil.substituteWithRealValues(fs.readFileSync(filename + '.headers').toString());
resHeaders = JSON.parse(headerContent);
}

Expand Down Expand Up @@ -123,7 +123,7 @@ module.exports.configure = function(mode) {
}

if (!forceLive) {
resBody = fs.readFileSync(filename);
resBody = sepiaUtil.substituteWithRealValues(fs.readFileSync(filename).toString());
}

req.emit('response', res);
Expand Down Expand Up @@ -191,7 +191,7 @@ module.exports.configure = function(mode) {
};

fs.writeFileSync(filename + '.headers',
JSON.stringify(headers, null, 2));
sepiaUtil.substituteWithOpaqueKeys(JSON.stringify(headers, null, 2)));
}

// Suppose the request times out while recording. We don't want the
Expand Down Expand Up @@ -232,7 +232,7 @@ module.exports.configure = function(mode) {
headers: res.headers
}, resBody);
} else {
fs.writeFileSync(filename, resBody);
fs.writeFileSync(filename,sepiaUtil.substituteWithOpaqueKeys(resBody.toString()));

// Store the request, if debug is true
if (debug) {
Expand Down
31 changes: 30 additions & 1 deletion lib/sepia/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function reset() {
path.join(process.cwd(), 'bins/');

globalOptions.filenameFilters = [];
globalOptions.substitutions = [];

globalOptions.includeHeaderNames = true;
globalOptions.headerWhitelist = [];
Expand Down Expand Up @@ -117,6 +118,32 @@ function addFilter(inFilter) {
globalOptions.filenameFilters.push(filter);
}

//
// substitutions
//
function addSubstitution(opaqueKey, actualValueFn) {
globalOptions.substitutions.push({opaqueKey: opaqueKey, actualValueFn: actualValueFn});
}

function substituteWithOpaqueKeys(text) {
var substitutions = globalOptions.substitutions;
for (var i=0; i<substitutions.length; i++) {
var subst = substitutions[i];
text = text.replace(subst.actualValueFn(), subst.opaqueKey);
}
return text;
}

function substituteWithRealValues(text) {
var substitutions = globalOptions.substitutions;
for (var i=0; i<substitutions.length; i++) {
var subst = substitutions[i];
text = text.replace(subst.opaqueKey, subst.actualValueFn());
}
return text;
}


// -- UTILITY FUNCTIONS --------------------------------------------------------

function mkdirpSync(folder) {
Expand Down Expand Up @@ -346,7 +373,6 @@ function constructFilename(method, reqUrl, reqBody, reqHeaders) {

logFixtureStatus(hashFile, hashParts);
touchOnHit(hashFile);

return hashFile;
}

Expand Down Expand Up @@ -440,6 +466,9 @@ module.exports.shouldForceLive = shouldForceLive;
module.exports.removeInternalHeaders = removeInternalHeaders;
module.exports.findTheBestMatchingFixture = findTheBestMatchingFixture;
module.exports.shouldFindMatchingFixtures = shouldFindMatchingFixtures;
module.exports.addSubstitution = addSubstitution;
module.exports.substituteWithRealValues = substituteWithRealValues;
module.exports.substituteWithOpaqueKeys = substituteWithOpaqueKeys;

module.exports.internal = {};
module.exports.internal.globalOptions = globalOptions;
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bokor",
"version": "1.0.1",
"version": "1.1.0",
"description": "Bokor is a simple, Record and Playback Mock Server written in Node.js, utilized for Service Virtualization.",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -38,6 +38,7 @@
"colors": "^1.1.2",
"express": "^4.14.0",
"http-proxy": "^1.15.2",
"levenshtein": "^1.0.5"
"levenshtein": "^1.0.5",
"moment": "^2.17.1"
}
}
Loading

0 comments on commit 09354f7

Please sign in to comment.