nails-boilerplate 0.8.2 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/application.js +1 -1
- package/lib/controller.js +11 -4
- package/lib/mongoose_connector.js +4 -2
- package/lib/nails.js +16 -15
- package/lib/router.js +73 -2
- package/package.json +12 -10
- package/spec/services/integration/app/controllers/mjs_controller.mjs +13 -0
- package/spec/services/integration/app/controllers/websocket_controller.js +13 -0
- package/spec/services/integration/config/routes.js +4 -1
- package/spec/services.integration.spec.js +43 -0
- package/templates/config/routes.js +3 -0
- package/templates/spec/home_controller.spec.js +15 -0
package/lib/application.js
CHANGED
package/lib/controller.js
CHANGED
|
@@ -60,8 +60,9 @@ class Controller {
|
|
|
60
60
|
* @param {object} params The parameter hash for this action
|
|
61
61
|
* @param {object} request The http request object from the http server
|
|
62
62
|
* @param {object} response The http response object from the http server
|
|
63
|
+
* @param {object} ws The WebSocket instance
|
|
63
64
|
*/
|
|
64
|
-
_do (action, params, request, response) {
|
|
65
|
+
_do (action, params, request, response, ws) {
|
|
65
66
|
request.handled_by_controller = true;
|
|
66
67
|
var doAction = this[action];
|
|
67
68
|
console.log(this.constructor.name, 'doing', action);
|
|
@@ -69,9 +70,15 @@ class Controller {
|
|
|
69
70
|
if (typeof doAction != 'function') {
|
|
70
71
|
//return this['404'](params, request, response);
|
|
71
72
|
var error = new Error('Action Not Available: #' + action);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
if (response) {
|
|
74
|
+
return response.status(404).send({
|
|
75
|
+
error: error.stack
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return ws.close(1003, "Action Not available: #" + action);
|
|
79
|
+
}
|
|
80
|
+
if (ws && !response) {
|
|
81
|
+
return doAction.call(this, params, ws, request);
|
|
75
82
|
}
|
|
76
83
|
// Override async with Controller definition.
|
|
77
84
|
if (doAction.async != undefined) params._async = !!doAction.async;
|
|
@@ -12,9 +12,11 @@ module.exports.connect = function(options) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
module.exports.generateModelSuperclass = function(name, options) {
|
|
15
|
-
let schema =
|
|
15
|
+
let schema = options.schema instanceof mongoose.Schema
|
|
16
|
+
? options.schema
|
|
17
|
+
: new mongoose.Schema(options.schema);
|
|
16
18
|
if (options.indexes) {
|
|
17
|
-
options.indexes.forEach(index => schema.
|
|
19
|
+
options.indexes.forEach(index => schema.index(index));
|
|
18
20
|
}
|
|
19
21
|
return mongoose.model(name, options.schema);
|
|
20
22
|
}
|
package/lib/nails.js
CHANGED
|
@@ -24,7 +24,7 @@ module.exports = nails;
|
|
|
24
24
|
|
|
25
25
|
function nails(app_config) {
|
|
26
26
|
nails.config = app_config.config;
|
|
27
|
-
configure(app_config);
|
|
27
|
+
application._onceConfigured = configure(app_config);
|
|
28
28
|
return {startServer: startServer};
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -33,7 +33,7 @@ nails.Controller = Controller;
|
|
|
33
33
|
nails.Model = ModelV2;
|
|
34
34
|
nails.ModelDeprecated = Model;
|
|
35
35
|
|
|
36
|
-
function configure( app_config ) {
|
|
36
|
+
async function configure( app_config ) {
|
|
37
37
|
express_app.set('nails_config', application);
|
|
38
38
|
application.config = app_config.config;
|
|
39
39
|
// TODO: may not need mimes any more.
|
|
@@ -66,8 +66,8 @@ function configure( app_config ) {
|
|
|
66
66
|
}
|
|
67
67
|
// TODO: make this Model style mandatory
|
|
68
68
|
if (DBConnector.connect && DBConnector.generateModelSuperclass) {
|
|
69
|
-
DBConnector.connect(app_config.db);
|
|
70
|
-
ModelV2.setConnector(DBConnector);
|
|
69
|
+
await DBConnector.connect(app_config.db);
|
|
70
|
+
await ModelV2.setConnector(DBConnector);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// init Controllers
|
|
@@ -80,15 +80,16 @@ function configure( app_config ) {
|
|
|
80
80
|
function startServer(config) {
|
|
81
81
|
// Log the config.
|
|
82
82
|
console.log(config);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
application._onceConfigured.then(() => {
|
|
84
|
+
// TODO: Use logging middleware.
|
|
85
|
+
|
|
86
|
+
// Use the router middleware.
|
|
87
|
+
express_app.use(application.router.express_router);
|
|
88
|
+
var ip = application.config.IP || 'localhost';
|
|
89
|
+
var port = application.config.PORT || 3000;
|
|
90
|
+
console.log("starting nails. listening to ", ip + ':' + port);
|
|
91
|
+
express_app.listen(port, ip);
|
|
92
|
+
});
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
// TODO: create an initializer lib file.
|
|
@@ -98,12 +99,12 @@ function init_controllers(controller_lib) {
|
|
|
98
99
|
function init_models(model_lib) {
|
|
99
100
|
init_app_lib(Model, model_lib);
|
|
100
101
|
}
|
|
101
|
-
function init_app_lib(superclass, abs_path) {
|
|
102
|
+
async function init_app_lib(superclass, abs_path) {
|
|
102
103
|
console.log('attempting to import:', abs_path);
|
|
103
104
|
if (!fs.existsSync(abs_path))
|
|
104
105
|
return console.log('Cannot initialize. Path not found.', abs_path);
|
|
105
106
|
if (fs.statSync(abs_path).isFile()) {
|
|
106
|
-
let subclass =
|
|
107
|
+
let subclass = (await import(abs_path)).default;
|
|
107
108
|
// Constructor function was provided
|
|
108
109
|
if (!superclass.isPrototypeOf(subclass))
|
|
109
110
|
return superclass.extend(subclass);
|
package/lib/router.js
CHANGED
|
@@ -6,6 +6,7 @@ var EventEmitter = require('events').EventEmitter;
|
|
|
6
6
|
var URL = require('url');
|
|
7
7
|
var path = require('path');
|
|
8
8
|
var querystring = require('querystring');
|
|
9
|
+
var expressWs = require('express-ws');
|
|
9
10
|
var app = require('./application.js');
|
|
10
11
|
//var nails = require('./nails.js');
|
|
11
12
|
var _extend = require('util')._extend;
|
|
@@ -20,6 +21,25 @@ var _extend = require('util')._extend;
|
|
|
20
21
|
* @param application the nails application singleton.
|
|
21
22
|
*/
|
|
22
23
|
class Router extends EventEmitter {
|
|
24
|
+
static get webSocketsEnabled() {
|
|
25
|
+
return this._webSocketsEnabled;
|
|
26
|
+
}
|
|
27
|
+
static set webSocketsEnabled(enabled) {
|
|
28
|
+
this._webSocketsEnabled = enabled;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static set expressWs(ews) {
|
|
32
|
+
this._expressWs = ews;
|
|
33
|
+
}
|
|
34
|
+
static get expressWs() {
|
|
35
|
+
return this._expressWs;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
enableWebSockets() {
|
|
39
|
+
if (Router.webSocketsEnabled) return;
|
|
40
|
+
Router.webSocketsEnabled = true;
|
|
41
|
+
Router.expressWs = expressWs(this.application);
|
|
42
|
+
}
|
|
23
43
|
constructor(routes, application) {
|
|
24
44
|
super();
|
|
25
45
|
this.application = application || app;
|
|
@@ -38,7 +58,12 @@ class Router extends EventEmitter {
|
|
|
38
58
|
var consequences = route[2];
|
|
39
59
|
var is_public = consequences && consequences.public;
|
|
40
60
|
console.log("setting route for", method, path_matcher, consequences);
|
|
41
|
-
if (
|
|
61
|
+
if (method.match(/ws/i)) {
|
|
62
|
+
this.enableWebSockets();
|
|
63
|
+
this.express_router.ws(
|
|
64
|
+
path_matcher,
|
|
65
|
+
this.get_websocket_handler(consequences));
|
|
66
|
+
} else if (consequences && consequences.public)
|
|
42
67
|
this.express_router.use(path_matcher,
|
|
43
68
|
this.application.static(this.application.get('public_root')));
|
|
44
69
|
else this.express_router[method](path_matcher,
|
|
@@ -52,6 +77,32 @@ class Router extends EventEmitter {
|
|
|
52
77
|
// Router.prototype.delete;
|
|
53
78
|
// end crud methods
|
|
54
79
|
|
|
80
|
+
// TODO: Combine this with the regular route method
|
|
81
|
+
routeWs(ws, request) {
|
|
82
|
+
console.log(
|
|
83
|
+
'\nSERVER_LOG:: WebSocket Router received request for: ',
|
|
84
|
+
request.method,
|
|
85
|
+
request.url,
|
|
86
|
+
'\n'
|
|
87
|
+
);
|
|
88
|
+
var params = _extend({}, request.params);
|
|
89
|
+
_extend(params, request.querty);
|
|
90
|
+
|
|
91
|
+
var controller = params._controller;
|
|
92
|
+
var action = params._action;
|
|
93
|
+
console.log('the action is:', controller, action, params);
|
|
94
|
+
console.log('the params are:', params);
|
|
95
|
+
console.log('ws router emitting:', 'dispatchTo:' + controller);
|
|
96
|
+
this.emit('dispatchTo:' + controller, action, params, request, null, ws);
|
|
97
|
+
|
|
98
|
+
if (!request.handled_by_controller) {
|
|
99
|
+
params.error = {message: 'controller ' + controller + ' does not exist'};
|
|
100
|
+
console.log('closing websocket');
|
|
101
|
+
return ws.close(1003, "Action Not available: #" + action);
|
|
102
|
+
//this.emit('dispatchTo:application', 404, params, request, null, ws);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
55
106
|
route(request, response) {
|
|
56
107
|
// TODO: use express logging.
|
|
57
108
|
console.log(
|
|
@@ -76,6 +127,26 @@ class Router extends EventEmitter {
|
|
|
76
127
|
this.emit('dispatchTo:application', 404, params, request, response);
|
|
77
128
|
}
|
|
78
129
|
|
|
130
|
+
get_websocket_handler(route_options) {
|
|
131
|
+
route_options = route_options || {};
|
|
132
|
+
return (ws, request) => {
|
|
133
|
+
console.log("handling a websocket request");
|
|
134
|
+
var controller = route_options['controller'];
|
|
135
|
+
var action = route_options['action'];
|
|
136
|
+
for (var i = 0; route_options[i]; i++) {
|
|
137
|
+
if (route_options[i] == 'action') action = action || request.params[i];
|
|
138
|
+
else if (route_options[i] == 'controller')
|
|
139
|
+
controller = controller || request.params[i];
|
|
140
|
+
else request.params[route_options[i]] = request.params[i];
|
|
141
|
+
}
|
|
142
|
+
request.params._action = action || request.params.action || "index";
|
|
143
|
+
request.params._controller =
|
|
144
|
+
controller || request.params.controller || "application";
|
|
145
|
+
|
|
146
|
+
this.routeWs(ws, request);
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
79
150
|
get_express_route_handler(route_options) {
|
|
80
151
|
route_options = route_options || {};
|
|
81
152
|
return (request, response) => {
|
|
@@ -109,7 +180,7 @@ class Router extends EventEmitter {
|
|
|
109
180
|
request.params._autorender = autorender ? !!autorender : true;
|
|
110
181
|
|
|
111
182
|
this.route(request, response);
|
|
112
|
-
}
|
|
183
|
+
};
|
|
113
184
|
}
|
|
114
185
|
}
|
|
115
186
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nails-boilerplate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "A node.js webserver scaffold",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -23,24 +23,26 @@
|
|
|
23
23
|
"rails"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"@babel/core": "*",
|
|
27
|
+
"@babel/preset-react": "*",
|
|
26
28
|
"ejs": "*",
|
|
27
29
|
"express": "*",
|
|
28
30
|
"express-react-views": "*",
|
|
29
|
-
"
|
|
30
|
-
"@babel/preset-react": "*",
|
|
31
|
+
"express-ws": "*",
|
|
31
32
|
"mime": "*",
|
|
32
|
-
"mongodb": "
|
|
33
|
+
"mongodb": "^3.5.7",
|
|
34
|
+
"mongoose": "*",
|
|
33
35
|
"react": "*",
|
|
34
|
-
"react-dom": "
|
|
35
|
-
"wrench": "*"
|
|
36
|
-
"mongoose": "*"
|
|
36
|
+
"react-dom": "^16.13.1",
|
|
37
|
+
"wrench": "*"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"mocha": ">= 0.4.0",
|
|
40
|
-
"sinon": "*",
|
|
41
40
|
"chai": "*",
|
|
42
41
|
"chai-http": "*",
|
|
43
|
-
"
|
|
42
|
+
"mocha": "^7.1.2",
|
|
43
|
+
"mongodb-memory-server": "*",
|
|
44
|
+
"sinon": "*",
|
|
45
|
+
"ws": "*"
|
|
44
46
|
},
|
|
45
47
|
"author": "Stanton W. Jones",
|
|
46
48
|
"license": "MIT"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
const Controller =
|
|
3
|
+
require("../../../../../index.js").Controller;
|
|
4
|
+
*/
|
|
5
|
+
import nails from "../../../../../index.js";
|
|
6
|
+
export default class MjsController extends nails.Controller {
|
|
7
|
+
// DO NOT OVERRIDE CONSTRUCTOR
|
|
8
|
+
index(params, request, response) {
|
|
9
|
+
response.json({
|
|
10
|
+
classbased_index: true
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const Controller =
|
|
2
|
+
require("../../../../../index.js").Controller;
|
|
3
|
+
module.exports = class WebsocketController extends Controller {
|
|
4
|
+
index(params, ws, request) {
|
|
5
|
+
ws.send("It worked");
|
|
6
|
+
ws.close();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
voodoo(params, ws, request) {
|
|
10
|
+
ws.send("Voodoo worked");
|
|
11
|
+
ws.close();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -31,7 +31,10 @@ var routes = [
|
|
|
31
31
|
// A test route which routes the first part of pathname to controller and the second to the action
|
|
32
32
|
['get', /^\/(\w+)\/(\w+)$/i, {0: 'controller', 1: 'action'}],
|
|
33
33
|
// Maps the first two parts of the path to controller and action, and the third to the id parameter
|
|
34
|
-
['get', "/:controller/:action/:id"]
|
|
34
|
+
['get', "/:controller/:action/:id"],
|
|
35
|
+
|
|
36
|
+
['ws', "/", {controller: 'websocket'}],
|
|
37
|
+
['ws', "/voodoo", {controller: 'websocket', action: 'voodoo'}]
|
|
35
38
|
];
|
|
36
39
|
|
|
37
40
|
module.exports = routes;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Import the dependencies for testing
|
|
2
2
|
var chai = require('chai');
|
|
3
3
|
var chaiHttp = require('chai-http');
|
|
4
|
+
const WebSocket = require('ws');
|
|
5
|
+
|
|
4
6
|
var nails = require('./services/integration/server.js');
|
|
5
7
|
var express_app = nails.application;
|
|
6
8
|
const assert = require('assert');
|
|
@@ -62,6 +64,47 @@ describe("Integration", function() {
|
|
|
62
64
|
});
|
|
63
65
|
})
|
|
64
66
|
});
|
|
67
|
+
|
|
68
|
+
describe("WebSockets", function() {
|
|
69
|
+
it("should listen on /", function(done) {
|
|
70
|
+
this.timeout(2000);
|
|
71
|
+
var requester = chai.request(express_app).keepOpen();
|
|
72
|
+
var wsClient = new WebSocket("ws://localhost:3333/");
|
|
73
|
+
|
|
74
|
+
wsClient.on('close', () => done());
|
|
75
|
+
wsClient.on('message', (message) => {
|
|
76
|
+
assert(message == "It worked");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
it("should listen on /voodoo", function(done) {
|
|
80
|
+
this.timeout(2000);
|
|
81
|
+
var requester = chai.request(express_app).keepOpen();
|
|
82
|
+
var wsClient = new WebSocket("ws://localhost:3333/voodoo");
|
|
83
|
+
|
|
84
|
+
wsClient.on('message', (message) => {
|
|
85
|
+
assert(message == "Voodoo worked");
|
|
86
|
+
});
|
|
87
|
+
wsClient.on('close', (code, reason) => done());
|
|
88
|
+
});
|
|
89
|
+
it("should not listen on /voodootwo", function(done) {
|
|
90
|
+
// TODO: this doesn't fail. Figure out why.
|
|
91
|
+
//done();
|
|
92
|
+
this.timeout(2000);
|
|
93
|
+
var requester = chai.request(express_app).keepOpen();
|
|
94
|
+
var wsClient = new WebSocket("ws://localhost:3333/voodootwo");
|
|
95
|
+
|
|
96
|
+
wsClient.on('close', (code, reason) => {
|
|
97
|
+
console.log('voodootwo closed', code, reason);
|
|
98
|
+
done();
|
|
99
|
+
});
|
|
100
|
+
debugger;
|
|
101
|
+
});
|
|
102
|
+
it("should be closed with the correct code if the action is absent");
|
|
103
|
+
it("should be closed with the correct code if the controller is absent");
|
|
104
|
+
it("should correctly parse the params");
|
|
105
|
+
it("should correctly handle dynamic controller and action");
|
|
106
|
+
});
|
|
107
|
+
|
|
65
108
|
describe("GET /json/:action", function() {
|
|
66
109
|
it('should render params if nothing is returned by the action',
|
|
67
110
|
function(done) {chai.request(express_app)
|
|
@@ -26,6 +26,9 @@ var routes = [
|
|
|
26
26
|
['get', /^\/(\w+)\/(\w+)$/i, {0: 'controller', 1: 'action'}],
|
|
27
27
|
// Maps the first two parts of the path to controller and action, and the third to the id parameter
|
|
28
28
|
['get', "/:controller/:action/:id"]
|
|
29
|
+
|
|
30
|
+
// Defines a WebSocket handler
|
|
31
|
+
['ws', "/:controller/:action/:id"]
|
|
29
32
|
];
|
|
30
33
|
|
|
31
34
|
module.exports = routes;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
var assert = require('assert');
|
|
2
|
+
var sinon = require('sinon');
|
|
3
|
+
var chai = require('chai');
|
|
4
|
+
var chaiHttp = require('chai-http');
|
|
5
|
+
var nails = require('../server.js');
|
|
6
|
+
var express_app = nails.application;
|
|
7
|
+
|
|
8
|
+
chai.use(chaiHttp);
|
|
9
|
+
chai.should();
|
|
10
|
+
|
|
11
|
+
describe("GET /", () => {
|
|
12
|
+
it('Should render the home page', done => {
|
|
13
|
+
|
|
14
|
+
});
|
|
15
|
+
});
|