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.
@@ -6,4 +6,4 @@ var app = express();
6
6
  module.exports = app;
7
7
 
8
8
  app.Router = express.Router;
9
- app.static = express.static;
9
+ app.static = express.static;
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
- return response.status(404).send({
73
- error: error.stack
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 = new mongoose.Schema(options.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.createIndex(index));
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
- // 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);
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 = require(abs_path);
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 (consequences && consequences.public)
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.8.2",
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
- "@babel/core": "*",
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
- "mongodb-memory-server": "*"
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
+ });