Skip to content
Open
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Projects use the following directory structure.
│ ├── services/
│ ├── middleware/
│ ├── swagger/
│ ├── test/
| | ├── bos-mocks/
| | | ├── middleware/
| | | ├── services/
```

#### Handlers
Expand Down Expand Up @@ -75,7 +79,7 @@ exports.getResult = function(num) {
return num;
}
};

```

We want to use that service from our handler, so we include `fizzbuzz` as a parameter of the `init` function.
Expand All @@ -90,7 +94,7 @@ exports.init = function(app, fizzbuzz) {
result: fizzbuzz.getResult(num)
});
});

}
```
#### Third-party Services
Expand Down Expand Up @@ -149,6 +153,10 @@ At a high-level, BlueOak Server's Swagger support provides the following:
* Multiple top-level Swagger API definitions supporting delivery of multiple API base paths
* Publishing of the fully compiled Swagger spec for input to by tools such as [`Swagger-UI`](http://swagger.io/swagger-ui/) and [`swagger-commander`](https://www.npmjs.com/package/swagger-commander)

#### Mocking

Services and middleware can be mocked for testing by creating mocks in the `test/bos-mocks/services` or `test/bos-mocks/middleware` directories. The mock file name should match the file name of the service or middleware you wish to mock. The implementation of a mock is no different than a normal service or middleware implementation. After you have implemented your mocks, you can instruct BlueOak Server to use them by specifying them as a comma-separated list in the `--mock-services` or `--mock-middleware` command line arguments. For example: `blueoak-server --mock-services service1,service2 --mock-middleware middleware1,middleware2`

### Installation

```bash
Expand Down
25 changes: 23 additions & 2 deletions bin/blueoak-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,37 @@
* Copyright (c) 2015-2016 PointSource, LLC.
* MIT Licensed
*/
var cli = require('commander');
var pkg = require('../package.json');
var server = require('../');

cli = parseOptions();

server.init({
appDir: process.cwd()
appDir: process.cwd(),
mocks: {
services: cli.mockServices,
middleware: cli.mockMiddleware
}
}, function(err) {
if (err) {
console.warn('Startup failed', err);
} else {
var logger = this.services.get('logger');
logger.info('Server started');
}

});

function parseOptions() {
// parse cli options
cli.version(pkg.version)
.option('--mock-services <services>', 'comma separated list of service names to mock', toList)
.option('--mock-middleware <middleware>', 'comma separated list of middleware to mock', toList)
.parse(process.argv);

return cli;
}

function toList(val) {
return val.split(',');
}
5 changes: 4 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ module.exports.init = function (opts, callback) {
global.__appDir = opts.appDir;
}

if (opts.mocks) {
global.__mocks = opts.mocks;
}

//Load the bootstrap services first (config and logging) since they're only needed for the master
initServices({bootstrap: true}, function (err) {
if (err) {
Expand Down Expand Up @@ -362,4 +366,3 @@ function checkNodeVersion(logger) {
module.exports.testUtility = function () {
return require('./testlib/util');
};

10 changes: 5 additions & 5 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ function createLoader() {
var modPath = path.resolve(dir, file);
var mod;
try {
mod = require(modPath);
mod = subRequire(modPath);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Middleware mocks were not loading because middleware was not loaded with subRequire. I did this to make it work, but admittedly do not understand all of the potential ramifications of this change.

} catch (e) {
throw new Error('Could not load ' + modPath + ': ' + e.message);
}
Expand Down Expand Up @@ -486,11 +486,11 @@ function createLoader() {
}

/*
* Strips the prefix + suffix underscores from the service name, allowing you to inject the service
* without having to think of an alternative name. For example, you can inject the logger service
* Strips the prefix + suffix underscores from the service name, allowing you to inject the service
* without having to think of an alternative name. For example, you can inject the logger service
* as "_logger_" and still be free to assign the service to the variable "logger".
*
* camelCamel strings are then coverted into dash-case. Based off of:
*
* camelCamel strings are then coverted into dash-case. Based off of:
* https://github.com/epeli/underscore.string/blob/master/dasherize.js
*/
function normalizeServiceName(str) {
Expand Down
46 changes: 45 additions & 1 deletion lib/subRequire.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* In this case we could call subRequire(Y, X), and it would require Y from the context of X.
*/
var path = require('path'),
util = require('util'),
fs = require('fs'),
_ = require('lodash');

var modulePath = {}; //maintain a list of the paths where we resolved files - needed for unloading the modules
Expand All @@ -25,6 +27,7 @@ var modulePath = {}; //maintain a list of the paths where we resolved files - ne
* but also provides a way to load submodules by passing the optional parentId argument.
*/
module.exports = function (id, parentId) {
id = resolveMock(id);

if (id.indexOf('.js') > -1) {
return loadJsFile(id);
Expand All @@ -45,6 +48,47 @@ module.exports.unload = function (id) {
}
};

//Attempts to resolve mock modules
function resolveMock(id, type) {
// short circuit if no mocks are declared
if (!global.__mocks) {
return id;
}

// recurse to resolve different mock types
if (arguments.length < 2) {
id = resolveMock(id, 'services');
id = resolveMock(id, 'middleware');
return id;
}

// if the mock type declares our mock, attempt to resolve it
if (global.__mocks[type] &&
_.includes(global.__mocks[type], path.basename(id, path.extname(id)))
) {
var mockId = path.basename(id);
var mockPath = path.resolve(global.__appDir, 'test', 'bos-mocks', type, mockId);
var mockExists = fs.existsSync(mockPath) || fs.existsSync(mockPath + '.js');

// if we have a mock path and the mock exists, load it
// otherwise, warn the user
if (mockPath && mockExists) {
id = mockPath;
} else if (!mockExists) {
var logger = this.services.get('logger');
if (logger) {
this.services.get('logger').warn(
util.format('The requested mock %s "%s" were not found in the mock %s directory (%s)',
type,
mockId,
type,
path.dirname(mockPath)
));
}
}
}
return id;
}

//Performs a require directly on a file
//A module id is calculated based on the name of the file, e.g.
Expand Down Expand Up @@ -108,4 +152,4 @@ function loadFromParent(id, parentId) {
var path = _.findWhere(parentMod.children, {exports: mod}).id;
modulePath[id] = path;
return mod;
}
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"base64url": "^1.0.4",
"body-parser": "^1.12.4",
"cfenv": "^1.0.0",
"commander": "^2.11.0",
"config": "^1.21.0",
"cookie-session": "^1.2.0",
"cors": "^2.6.0",
Expand Down Expand Up @@ -73,7 +74,8 @@
"eslint": "^3.3.1",
"istanbul": "^0.4.1",
"mocha": "^3.0.2",
"mocha-jenkins-reporter": "^0.1.8"
"mocha-jenkins-reporter": "^0.1.8",
"request-promise": "^4.2.1"
},
"files": [
"bin",
Expand Down
10 changes: 10 additions & 0 deletions test/integration/fixtures/server12/config/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"express": {
"port": "5000",
"middleware": ["pet-middleware1", "pet-middleware2"]
},

"cluster": {
"maxWorkers": 1
}
}
28 changes: 28 additions & 0 deletions test/integration/fixtures/server12/handlers/petstore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2015-2016 PointSource, LLC.
* MIT Licensed
*/
var service1,
service2,
serviceModule;

exports.init = function (petService1, petService2, petServiceModule) {
service1 = petService1;
service2 = petService2;
serviceModule = petServiceModule;
};

exports.getPets1 = function (req, res, next) {
res.send(service1.getPets());
next();
};

exports.getPets2 = function (req, res, next) {
res.send(service2.getPets());
next();
};

exports.getPets3 = function (req, res, next) {
res.send(serviceModule.getPets());
next();
};
10 changes: 10 additions & 0 deletions test/integration/fixtures/server12/middleware/pet-middleware1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
exports.init = function (app, config, logger, callback) {
app.use(middleware);
logger.info('Pet Middleware1 initialized');
callback();
};

function middleware(req, res, next) {
res.header('x-pet-middleware1', 'pet-middleware1');
next();
}
10 changes: 10 additions & 0 deletions test/integration/fixtures/server12/middleware/pet-middleware2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
exports.init = function (app, config, logger, callback) {
app.use(middleware);
logger.info('Pet Middleware2 initialized');
callback();
};

function middleware(req, res, next) {
res.header('x-pet-middleware2', 'pet-middleware2');
next();
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions test/integration/fixtures/server12/services/pet-service1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2015-2016 PointSource, LLC.
* MIT Licensed
*/
exports.init = function(logger) {
logger.info('Pet Service1 initialized');
};

exports.getPets = function() {
return {
id: 1,
name: 'service1 pet'
};
};
14 changes: 14 additions & 0 deletions test/integration/fixtures/server12/services/pet-service2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2015-2016 PointSource, LLC.
* MIT Licensed
*/
exports.init = function(logger) {
logger.info('Pet Service2 initialized');
};

exports.getPets = function() {
return {
id: 2,
name: 'service2 pet'
};
};
Loading