@projectinvicta/nails 2.0.15 → 3.0.0
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/README.md +70 -84
- package/bin/test/test_init.sh +33 -0
- package/index.ts +11 -0
- package/lib/Controller.ts +207 -0
- package/lib/Nails.ts +210 -0
- package/lib/Router.ts +180 -0
- package/lib/{application.js → application.ts} +8 -3
- package/lib/config.ts +74 -0
- package/package.json +14 -11
- package/spec/controller.spec.js +5 -51
- package/spec/nails.spec.js +1 -0
- package/spec/router.spec.js +5 -5
- package/spec/services/integration/config/db.ts +7 -0
- package/spec/services/integration/config/service.js +8 -8
- package/spec/services/integration/server/controllers/classbased_controller.js +2 -2
- package/spec/services/integration/server/controllers/default_json_controller.js +19 -7
- package/spec/services/integration/server/controllers/error_controller.js +2 -2
- package/spec/services/integration/server/controllers/home_controller.js +6 -28
- package/spec/services/integration/server/controllers/json_controller.js +2 -2
- package/spec/services/integration/server/controllers/manualrenderasync_controller.js +2 -2
- package/spec/services/integration/server/controllers/mjs_controller.mjs +2 -6
- package/spec/services/integration/server/controllers/modeltest_controller.js +6 -9
- package/spec/services/integration/server/controllers/websocket_controller.js +1 -1
- package/spec/services/integration/server/models/dog.js +7 -5
- package/spec/services/integration/server/models/owner.js +13 -0
- package/spec/services/integration/server.js +3 -4
- package/spec/services.integration.spec.js +45 -17
- package/templates/default/config/db.ts +13 -0
- package/{spec/services/integration_sequelize/config/mimes.js → templates/default/config/mimes.ts} +42 -61
- package/templates/default/config/routes.ts +22 -0
- package/templates/{config/service.js → default/config/service.ts} +2 -2
- package/templates/default/package.json +8 -2
- package/templates/default/public/README.xml +68 -85
- package/templates/default/server/controllers/home_controller.js +2 -2
- package/templates/{server/controllers/home_controller.js → default/server/controllers/users_controller.ts} +29 -34
- package/templates/default/server/models/Dog.ts +8 -0
- package/templates/default/server/models/User.ts +14 -0
- package/templates/default/spec/User.test.js +7 -5
- package/templates/default/spec/home_controller.test.js +3 -3
- package/index.js +0 -6
- package/lib/collection.js +0 -6
- package/lib/controller.js +0 -182
- package/lib/database_connector.js +0 -12
- package/lib/firebase_connector.js +0 -94
- package/lib/model_v2.js +0 -24
- package/lib/mongoose_connector.js +0 -49
- package/lib/mongoose_mem_connector.js +0 -21
- package/lib/nails.js +0 -244
- package/lib/router.js +0 -202
- package/lib/sequelize_connector.js +0 -31
- package/lib/server.js +0 -1
- package/spec/model_v2.spec.js +0 -75
- package/spec/mongodb_connector.util.js +0 -30
- package/spec/mongoose_connector.util.js +0 -20
- package/spec/sequelize_connector.spec.js +0 -91
- package/spec/sequelize_connector.util.js +0 -18
- package/spec/services/integration/config/db.js +0 -14
- package/spec/services/integration_sequelize/README.md +0 -5
- package/spec/services/integration_sequelize/client/css/styles.css +0 -0
- package/spec/services/integration_sequelize/client/download.jpg +0 -0
- package/spec/services/integration_sequelize/client/favicon.ico +0 -0
- package/spec/services/integration_sequelize/client/index.html +0 -9
- package/spec/services/integration_sequelize/client/js/client.js +0 -0
- package/spec/services/integration_sequelize/client/js/components/app.jsx +0 -15
- package/spec/services/integration_sequelize/config/db.js +0 -4
- package/spec/services/integration_sequelize/config/routes.js +0 -25
- package/spec/services/integration_sequelize/config/service.js +0 -48
- package/spec/services/integration_sequelize/config/ssl/certificate.pem +0 -22
- package/spec/services/integration_sequelize/config/ssl/csr.csr +0 -17
- package/spec/services/integration_sequelize/config/ssl/key.pem +0 -28
- package/spec/services/integration_sequelize/config/ssl/private_key.pem +0 -30
- package/spec/services/integration_sequelize/config/ssl/public_key.pem +0 -9
- package/spec/services/integration_sequelize/package.json +0 -23
- package/spec/services/integration_sequelize/server/controllers/default_json_controller.js +0 -21
- package/spec/services/integration_sequelize/server/controllers/home_controller.js +0 -39
- package/spec/services/integration_sequelize/server/models/dog.js +0 -7
- package/spec/services/integration_sequelize/server/models/owner.js +0 -10
- package/spec/services/integration_sequelize/server/views/defaultjson/testnojson.ejs +0 -1
- package/spec/services/integration_sequelize/server/views/testreact/testreact.ejs +0 -15
- package/spec/services/integration_sequelize/server.js +0 -9
- package/spec/services.integration_sequelize.spec.js +0 -60
- package/templates/bin/promote.sh +0 -20
- package/templates/bin/rollout.sh +0 -74
- package/templates/bin/server.js +0 -6
- package/templates/bin/start.sh +0 -16
- package/templates/common/readme_fetcher.js +0 -4
- package/templates/config/db.js +0 -19
- package/templates/config/mimes.js +0 -59
- package/templates/config/routes.js +0 -38
- package/templates/config/ssl/certificate.pem +0 -22
- package/templates/config/ssl/csr.csr +0 -17
- package/templates/config/ssl/key.pem +0 -28
- package/templates/config/ssl/private_key.pem +0 -30
- package/templates/config/ssl/public_key.pem +0 -9
- package/templates/default/config/db.js +0 -19
- package/templates/default/config/mimes.js +0 -59
- package/templates/default/config/routes.js +0 -38
- package/templates/default/config/service.js +0 -45
- package/templates/default/server/models/User.js +0 -18
- package/templates/package-lock.json +0 -9048
- package/templates/package.json +0 -43
- package/templates/public/README.xml +0 -332
- package/templates/public/css/styles.css +0 -17
- package/templates/public/download.jpg +0 -0
- package/templates/public/favicon.ico +0 -0
- package/templates/public/index.html +0 -9
- package/templates/public/js/client.js +0 -1
- package/templates/server/models/User.js +0 -18
- package/templates/server/views/home/index.ejs +0 -14
- package/templates/server/views/partials/javascripts.ejs +0 -1
- package/templates/server/views/partials/reactapp.ejs +0 -1
- package/templates/server/views/partials/styles.ejs +0 -3
- package/templates/spec/User.test.js +0 -20
- package/templates/spec/home_controller.test.js +0 -28
- package/templates/spec/setupTests.js +0 -0
- package/templates/src/AboutPage.jsx +0 -9
- package/templates/src/HomePage.jsx +0 -9
- package/templates/src/Layout.jsx +0 -78
- package/templates/src/ReadmePage.jsx +0 -7
- package/templates/src/app.jsx +0 -29
- package/templates/src/components/ReadmeLoader.jsx +0 -13
- package/templates/src/styles/appstyles.css +0 -3
- package/templates/vite.config.ts +0 -42
|
@@ -1,57 +1,48 @@
|
|
|
1
|
-
<h1 id="
|
|
2
|
-
<p>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<
|
|
1
|
+
<h1 id="nailsanodewebserviceframework">Nails: A Node Webservice Framework</h1>
|
|
2
|
+
<p>Nails is a lightweight, configurable MVC-inspired backend framework for Node.js. With minimal dependencies, Nails offers a familiar syntax for developers coming from frameworks like Ruby on Rails or Django, while providing the flexibility of JavaScript.</p>
|
|
3
|
+
<p>Nails provides the essential building blocks to get your MVC-style application up and running quickly. The modules used in Nails can be easily extended to produce the custom functionality to fit your needs, and you are encouraged to do so.</p>
|
|
4
|
+
<h2 id="gettingstarted">Getting Started</h2>
|
|
5
|
+
<p>There are two recommended ways to get started with Nails.</p>
|
|
6
|
+
<h3 id="usingnpxrecommended">Using NPX (Recommended)</h3>
|
|
7
|
+
<p>You can create a new Nails project without a global installation using <code>npx</code>:</p>
|
|
8
|
+
<pre><code class="bash language-bash">npx @projectinvicta/nails init <app_name>
|
|
9
|
+
</code></pre>
|
|
10
|
+
<h3 id="usingglobalinstall">Using Global Install</h3>
|
|
11
|
+
<p>Alternatively, you can install the package globally:</p>
|
|
12
|
+
<pre><code class="bash language-bash">npm install -g @projectinvicta/nails
|
|
11
13
|
|
|
12
14
|
nails init <app_name>
|
|
13
15
|
</code></pre>
|
|
14
|
-
<p>
|
|
15
|
-
|
|
16
|
-
getting started. Additional controllers and views will automatically be imported
|
|
17
|
-
into nails. Now just hook the new controllers in with some new routes and you're
|
|
18
|
-
off to a good start.</p>
|
|
19
|
-
<pre><code>cd app_name
|
|
20
|
-
|
|
16
|
+
<p>After initializing your app, navigate into the new directory and start the server:</p>
|
|
17
|
+
<pre><code class="bash language-bash">cd <app_name>
|
|
21
18
|
npm install
|
|
22
|
-
|
|
23
19
|
npm start
|
|
24
20
|
</code></pre>
|
|
25
21
|
<h2 id="gettingtoknowyournailsservice">Getting to know your Nails service</h2>
|
|
26
22
|
<p>For your convenience, here is a quick outline of the main components of a nails service.
|
|
27
23
|
Remember: each object comes with an example file to use for reference when building your service.</p>
|
|
28
|
-
<h3 id="
|
|
29
|
-
<p>
|
|
30
|
-
<pre><code>
|
|
31
|
-
routes.js
|
|
32
|
-
db.js
|
|
24
|
+
<h3 id="namedexports">Named Exports</h3>
|
|
25
|
+
<p><code>nails</code> now provides a number of useful named exports. You can import them as follows:</p>
|
|
26
|
+
<pre><code class="js language-js">import Nails, { Controller, Model, DataTypes, /* config types */ } from '@projectinvicta/nails';
|
|
33
27
|
</code></pre>
|
|
28
|
+
<h3 id="config">Config</h3>
|
|
29
|
+
<p>Your configuration files are stored in app_name/config/.</p>
|
|
34
30
|
<p>Each default config file is annotated with comments documenting each field to
|
|
35
31
|
help you tailor your service to your needs.</p>
|
|
36
32
|
<h4 id="servicejs">service.js</h4>
|
|
37
33
|
<p>service.js contains information necessary to run your server. By default, it
|
|
38
|
-
specifies the port and the location of important libraries
|
|
39
|
-
values in different runtime environments, add a child object.</p>
|
|
34
|
+
specifies the port and the location of important libraries.</p>
|
|
40
35
|
<pre><code class="js language-js">export default {
|
|
41
36
|
...
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
37
|
+
SERVER_ROOT: "/path/to/my/nails/service",
|
|
38
|
+
PORT: 3333,
|
|
39
|
+
SSL_PORT: 3334,
|
|
46
40
|
}
|
|
47
41
|
</code></pre>
|
|
48
|
-
<p>Nails checks the NODE<em>ENV environment variable. If a matching child config
|
|
49
|
-
object is present, then those values will override the parent config. In the
|
|
50
|
-
above example, PORT will be overridden to 80 if NODE</em>ENV is set to PROD.</p>
|
|
51
42
|
<p>While most of these values don't need to be changed, feel free to add custom
|
|
52
43
|
fields. The resulting config will be available to your service through the nails
|
|
53
44
|
module:</p>
|
|
54
|
-
<pre><code class="js language-js">import
|
|
45
|
+
<pre><code class="js language-js">import Nails from '@projectinvicta/nails';
|
|
55
46
|
|
|
56
47
|
const service_config = nails.config
|
|
57
48
|
</code></pre>
|
|
@@ -66,7 +57,7 @@ const service_config = nails.config
|
|
|
66
57
|
<code>'yourCustomValue'</code>.</p>
|
|
67
58
|
<h4 id="routesjs">routes.js</h4>
|
|
68
59
|
<p><em>routes.js</em> is a list defining mappings from a url path to a <em>Controller</em> and
|
|
69
|
-
<em>Action</em>. Each entry in the list is an array with three elements:
|
|
60
|
+
<em>Action</em>. Routes are now strongly typed (see <code>lib/config.ts</code> and <code>lib/Router.ts</code>). Each entry in the list is an array with three elements:
|
|
70
61
|
<code>[method, path, options]</code></p>
|
|
71
62
|
<p><strong>method</strong> is a string defining the HTTP request method of the route. Supported
|
|
72
63
|
methods are <em>GET</em>, <em>PUT</em>, <em>POST</em>, <em>DELETE</em>, and <em>ALL</em>. All is a special case
|
|
@@ -109,24 +100,18 @@ than indices. You can name your regex captures "controller" and/or "action"
|
|
|
109
100
|
to dynamically route your request to the appropriate handler.</li>
|
|
110
101
|
</ul>
|
|
111
102
|
<h4 id="dbjs">db.js</h4>
|
|
112
|
-
<p>Quickly configure your database connection here.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
version of your desired sql database. Check out <a href="https://sequelize.org">Sequelize</a>
|
|
116
|
-
for more info.</p>
|
|
117
|
-
<p>Alternatively, you can configure a connection to MongoDB using the mongoose_connector.js.
|
|
118
|
-
If enabled, models will accept <a href="https://mongoosejs.com/docs/">Mongoose</a> schemas and will
|
|
119
|
-
be backed by the desired MongoDB. Consider using the in-memory DB during development.</p>
|
|
103
|
+
<p>Quickly configure your database connection here. The initial setup uses a <em>sqlite3</em> database file <code>config/development.db</code>
|
|
104
|
+
and an in-memory database in the test environment. Change the address to change the location and
|
|
105
|
+
version of your desired sql database. Check out <a href="https://sequelize.org">Sequelize</a> for more info.</p>
|
|
120
106
|
<h2 id="controller">Controller</h2>
|
|
121
107
|
<p>Controllers are defined in app/controllers/. Each controller module should
|
|
122
108
|
define a Controller subclass. The name will be used to match routes defined in
|
|
123
109
|
config/routes.js for incoming requests. Methods on the controller can be used as
|
|
124
110
|
actions, receiving <strong>params</strong>, <strong>request</strong>, and <strong>response</strong> as arguments.</p>
|
|
125
111
|
<p>For Example:</p>
|
|
126
|
-
<pre><code class="js language-js"
|
|
127
|
-
import nails from 'nails-boilerplate';
|
|
112
|
+
<pre><code class="js language-js">import { Controller } from '@projectinvicta/nails';
|
|
128
113
|
|
|
129
|
-
class HomeController extends
|
|
114
|
+
class HomeController extends Controller {
|
|
130
115
|
index(params, request, response) {
|
|
131
116
|
// default action
|
|
132
117
|
}
|
|
@@ -154,7 +139,9 @@ object returned by the getData function. If the route is changed to:</p>
|
|
|
154
139
|
<p><code>["get", "/data", {action: 'getData', json: true}],</code></p>
|
|
155
140
|
<p>it will accept GET requests to /data instead. All local routes are
|
|
156
141
|
implicitly routed to their respective parent controllers.</p>
|
|
157
|
-
<pre><code class="js language-js">
|
|
142
|
+
<pre><code class="js language-js">import { Controller } from '@projectinvicta/nails';
|
|
143
|
+
|
|
144
|
+
export default class UsersController extends Controller {
|
|
158
145
|
routes = [
|
|
159
146
|
// Routes requests to /absolute/path
|
|
160
147
|
['get', '/absolute/path', {action: 'actionA'}],
|
|
@@ -226,8 +213,11 @@ overridden to allow for the rendering of views by name.</p>
|
|
|
226
213
|
<pre><code class="js language-js">['get', '/your/json/route', {json: true}],
|
|
227
214
|
</code></pre>
|
|
228
215
|
<h5 id="apicontrollers">API Controllers</h5>
|
|
229
|
-
<p>By setting <code>json</code> to <code>true</code>, all actions in a controller will respond with JSON
|
|
230
|
-
<
|
|
216
|
+
<p>By setting <code>json</code> to <code>true</code>, all actions in a controller will respond with JSON by default.
|
|
217
|
+
This can be overridden for individual routes by setting <code>{json: false}</code> in the route options.</p>
|
|
218
|
+
<pre><code class="js language-js">import { Controller } from '@projectinvicta/nails';
|
|
219
|
+
|
|
220
|
+
class YourApiController extends Controller {
|
|
231
221
|
json = true;
|
|
232
222
|
|
|
233
223
|
action(params, request, response) {
|
|
@@ -236,23 +226,24 @@ overridden to allow for the rendering of views by name.</p>
|
|
|
236
226
|
}
|
|
237
227
|
</code></pre>
|
|
238
228
|
<h2 id="model">Model</h2>
|
|
239
|
-
<p>Models are programmatic representations of data you wish to persist in a
|
|
240
|
-
database
|
|
241
|
-
<code>options</code> object which is passed to the database connector module.</p>
|
|
229
|
+
<p>Models are programmatic representations of data you wish to persist in a database. Nails connects to
|
|
230
|
+
your database of choice using the Sequelize ORM.</p>
|
|
242
231
|
<h3 id="sequelizemodels">Sequelize Models</h3>
|
|
243
232
|
<p>Sequelize models are subclasses of
|
|
244
233
|
<a href="https://sequelize.org/docs/v6/core-concepts/model-basics/">Sequelize Models</a>, and come with the <code>count()</code>, <code>findAll()</code>,
|
|
245
234
|
and <code>create()</code> methods, to name a few. You can define your own models by
|
|
246
|
-
extending an instance of the <code>Model</code> class provided by Nails
|
|
247
|
-
<
|
|
248
|
-
import
|
|
249
|
-
|
|
250
|
-
|
|
235
|
+
extending an instance of the <code>Model</code> class provided by Nails. Model files must export the Model subclass as a default export and a named <code>schema</code> export.
|
|
236
|
+
They can also export an <code>options</code> object to provide ModelInitializationOptions as well as <code>defer</code>, <code>finalize</code>, and <code>migrate</code> functions.</p>
|
|
237
|
+
<pre><code class="js language-js">import { Model, DataTypes } from '@projectinvicta/nails';
|
|
238
|
+
|
|
239
|
+
// REQUIRED
|
|
240
|
+
export const schema = {
|
|
251
241
|
name: {type: DataTypes.STRING, allowNull: false},
|
|
252
242
|
email: {type: DataTypes.STRING, allowNull: false}
|
|
253
243
|
};
|
|
254
244
|
|
|
255
|
-
|
|
245
|
+
// Optional
|
|
246
|
+
export const options = {
|
|
256
247
|
indexes: [
|
|
257
248
|
{
|
|
258
249
|
unique: true,
|
|
@@ -261,30 +252,34 @@ options = {
|
|
|
261
252
|
],
|
|
262
253
|
};
|
|
263
254
|
|
|
264
|
-
|
|
255
|
+
// REQUIRED
|
|
256
|
+
export default class User extends Model {
|
|
265
257
|
someHelperMethod() {
|
|
266
258
|
// This method will be available on all instances of User and is
|
|
267
259
|
// an ideal way to simplify data manipulation.
|
|
268
260
|
}
|
|
269
261
|
};
|
|
262
|
+
|
|
263
|
+
// Optional
|
|
264
|
+
export async function defer(models) {
|
|
265
|
+
// define associations here
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Optional
|
|
269
|
+
export async function finalize(models) {
|
|
270
|
+
// any final model setup
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Optional
|
|
274
|
+
export async function migrate(queryInterface) {
|
|
275
|
+
// migration logic
|
|
276
|
+
}
|
|
270
277
|
</code></pre>
|
|
271
|
-
<h3 id="mongoosemodels">Mongoose Models</h3>
|
|
272
|
-
<p>Mongoose models are subclasses of
|
|
273
|
-
<a href="https://mongoosejs.com/docs/api/model.html">Mongoose Models</a>, and come with the <code>save()</code>, <code>find()</code>,
|
|
274
|
-
and <code>where()</code> methods, to name a few. You can define your own models by
|
|
275
|
-
extending an instance of the <code>Model</code> class provided by Nails:</p>
|
|
276
|
-
<pre><code class="js language-js">// const Model = require("nails-boilerplate").Model;
|
|
277
|
-
import nails from 'nails-boilerplate';
|
|
278
|
-
const userSchema = {name: String, email: String};
|
|
279
|
-
export default class User extends new Model("User", {schema: userSchema}) {
|
|
280
|
-
// Define your helper methods here
|
|
281
|
-
};
|
|
282
|
-
</code></pre>
|
|
283
|
-
<p>The <code>schema</code> option for Mongoose Models accepts a schema field that is used
|
|
284
|
-
to define how documents are stored in MongoDB.</p>
|
|
285
278
|
<h3 id="modellibrary">Model Library</h3>
|
|
286
279
|
<p>Nails will store all instantialized models in a single object called <code>MODELS</code>. By accessing these models via the library, you can avoid circular dependencies and ensure all models have been fully initialized.</p>
|
|
287
|
-
<pre><code class="js language-js">
|
|
280
|
+
<pre><code class="js language-js">import Nails, { Model } from '@projectinvicta/nails';
|
|
281
|
+
// ...
|
|
282
|
+
class User extends Model {
|
|
288
283
|
// A helper method which depends on anoher model using the
|
|
289
284
|
// Nails Model Library rather than directly importing the model.
|
|
290
285
|
async findFriends() {
|
|
@@ -293,18 +288,6 @@ to define how documents are stored in MongoDB.</p>
|
|
|
293
288
|
}
|
|
294
289
|
</code></pre>
|
|
295
290
|
<p>This design pattern is not always necessary, but will help avoid circular dependencies.</p>
|
|
296
|
-
<h3 id="databaseconnectors">Database Connectors</h3>
|
|
297
|
-
<p>Database connectors are intermediaries which define how a Model interacts with
|
|
298
|
-
a database. Database connector modules need to export two methods:</p>
|
|
299
|
-
<ul>
|
|
300
|
-
<li><em>connect(db</em>config)_ uses the db config defined in <em>db.js</em> to connect to
|
|
301
|
-
a database. This function will be called once by Nails.</li>
|
|
302
|
-
<li><em>generateModelSuperclass(name, options)</em> uses the provided Model name and
|
|
303
|
-
options to generate a Model prototype for use as an interface. A Model
|
|
304
|
-
interface is generated for each of your models, allowing them to interact with
|
|
305
|
-
a database. Ideally, interfaces will define save() and find() methods, but
|
|
306
|
-
these methods and their implementations are up to the individual connector.</li>
|
|
307
|
-
</ul>
|
|
308
291
|
<h2 id="view">View</h2>
|
|
309
292
|
<p>Views are dynamic templates used to render an html response for a browser.
|
|
310
293
|
Nails comes prepackaged with EJS templates.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {Controller} from "@projectinvicta/nails";
|
|
2
2
|
|
|
3
|
-
export default class HomeController extends
|
|
3
|
+
export default class HomeController extends Controller {
|
|
4
4
|
/**
|
|
5
5
|
* You can define a local routing table directly in the controller.
|
|
6
6
|
* Local routes take precidence over global routes. All local routes
|
|
@@ -1,34 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
getData(params, request, response) {
|
|
32
|
-
return {testData: Math.floor(Math.random() * 10000000)}
|
|
33
|
-
}
|
|
34
|
-
};
|
|
1
|
+
import {Controller} from "@projectinvicta/nails";
|
|
2
|
+
import User from "../models/User";
|
|
3
|
+
|
|
4
|
+
export default class UsersController extends Controller {
|
|
5
|
+
/**
|
|
6
|
+
* You can define a local routing table directly in the controller.
|
|
7
|
+
* Local routes take precidence over global routes. All local routes
|
|
8
|
+
* are prefixed with the controller name unless they start with '/'.
|
|
9
|
+
* For example, in HomeController the following route:
|
|
10
|
+
*
|
|
11
|
+
* ["get", "data", {action: 'getData', json: true}],
|
|
12
|
+
*
|
|
13
|
+
* will accept GET requests to /home/data and respond with the json
|
|
14
|
+
* object returned by the getData function. If the route is changed to:
|
|
15
|
+
*
|
|
16
|
+
* ["get", "/data", {action: 'getData', json: true}],
|
|
17
|
+
*
|
|
18
|
+
* it will accept GET requests to /data instead. All local routes are
|
|
19
|
+
* implicitly routed to their respective parent controllers.
|
|
20
|
+
*/
|
|
21
|
+
routes = [
|
|
22
|
+
["get", "./list"],
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
async list(params, request, response) {
|
|
26
|
+
return await User.findAll();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Model, DataTypes} from "@projectinvicta/nails";
|
|
2
|
+
import Dog from "./Dog";
|
|
3
|
+
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: DataTypes.STRING,
|
|
6
|
+
verified: DataTypes.BOOLEAN,
|
|
7
|
+
email: DataTypes.STRING,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default class User extends Model {};
|
|
11
|
+
|
|
12
|
+
export async function defer() {
|
|
13
|
+
await User.hasMany(Dog);
|
|
14
|
+
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import Nails from "@projectinvicta/nails";
|
|
2
|
+
import User from "#models/User";
|
|
3
|
+
import service_config from '#config/service.js';
|
|
3
4
|
import { beforeAll, test, expect } from "vitest";
|
|
4
5
|
|
|
5
6
|
const TEST_USER_EMAIL = "test@test.com";
|
|
6
7
|
const TEST_USER_NAME = "JohnDoe";
|
|
7
8
|
beforeAll(async () => {
|
|
8
9
|
// Only initialize the Models.
|
|
9
|
-
await
|
|
10
|
+
await new Nails(service_config).configure();
|
|
11
|
+
// await nails.MODELS.init( service_config );
|
|
10
12
|
});
|
|
11
13
|
|
|
12
14
|
test("Can create a User", async () => {
|
|
13
|
-
const user = await
|
|
15
|
+
const user = await User.create({name: TEST_USER_NAME, email: TEST_USER_EMAIL});
|
|
14
16
|
|
|
15
17
|
expect(user.id).toBeDefined();
|
|
16
|
-
const retrievedUser = await
|
|
18
|
+
const retrievedUser = await User.findByPk(user.id);
|
|
17
19
|
expect(retrievedUser.id).toBeDefined();
|
|
18
20
|
expect(retrievedUser.name).toEqual(TEST_USER_NAME);
|
|
19
21
|
expect(retrievedUser.email).toEqual(TEST_USER_EMAIL);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Nails from "@projectinvicta/nails";
|
|
2
2
|
// import chai from 'chai';
|
|
3
3
|
import {default as chaiHttp, request} from 'chai-http';
|
|
4
4
|
import service_config from '../config/service.js';
|
|
@@ -8,8 +8,8 @@ let express_app;
|
|
|
8
8
|
|
|
9
9
|
beforeAll(async () => {
|
|
10
10
|
// Initialize the application and start the server
|
|
11
|
-
|
|
12
|
-
express_app =
|
|
11
|
+
await new Nails( service_config ).startServer();
|
|
12
|
+
express_app = Nails.application;
|
|
13
13
|
chai.use(chaiHttp);
|
|
14
14
|
chai.should();
|
|
15
15
|
})
|
package/index.js
DELETED
package/lib/collection.js
DELETED
package/lib/controller.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
// const fs = require('fs');
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import events from 'node:events';
|
|
4
|
-
// const events = require('events');
|
|
5
|
-
import application from './application.js';
|
|
6
|
-
// const application = require('./application');
|
|
7
|
-
|
|
8
|
-
var router;
|
|
9
|
-
var controller_proto;
|
|
10
|
-
var models;
|
|
11
|
-
|
|
12
|
-
// TODO: refractor error generation
|
|
13
|
-
var NAME_REQUIRED_ERROR = function () {
|
|
14
|
-
return new Error(
|
|
15
|
-
'FATAL ERROR::: Named function required for Controller constructor method');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const DISABLE_AUTORENDER = new (class DisableAutorender {});
|
|
19
|
-
|
|
20
|
-
// The base controller definition
|
|
21
|
-
class Controller {
|
|
22
|
-
constructor() {
|
|
23
|
-
var subclassName = this.constructor.name;
|
|
24
|
-
var controllerName = this._getControllerName();
|
|
25
|
-
// subclassName.toLowerCase().replace(/controller$/, '');
|
|
26
|
-
router.removeAllListeners('dispatchTo:' + controllerName);
|
|
27
|
-
router.on('dispatchTo:' + controllerName, this._do.bind(this));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static get DISABLE_AUTORENDER() {
|
|
31
|
-
return DISABLE_AUTORENDER;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static setRouter (router_singleton) {
|
|
35
|
-
Controller.router = router = router_singleton;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
static setModels (models) {
|
|
39
|
-
Controller.prototype.models = models;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
static extend (constructor) {
|
|
43
|
-
console.log('extending', constructor.name);
|
|
44
|
-
if (!constructor.name) throw NAME_REQUIRED_ERROR();
|
|
45
|
-
controller_proto = controller_proto || new Controller();
|
|
46
|
-
constructor.prototype.__proto__ = controller_proto;
|
|
47
|
-
var constructed = new constructor();
|
|
48
|
-
|
|
49
|
-
// configure event listeners on router.
|
|
50
|
-
var controller_name =
|
|
51
|
-
constructor.name.toLowerCase().replace(/controller$/, '');
|
|
52
|
-
router.removeAllListeners('dispatchTo:' + controller_name);
|
|
53
|
-
router.on('dispatchTo:' + controller_name, constructed._do.bind(constructed));
|
|
54
|
-
|
|
55
|
-
return constructed;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
_getControllerName() {
|
|
59
|
-
return this.constructor.name.toLowerCase().replace(/controller$/, '');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Initializes local and global routes defined on the Controller subclass */
|
|
63
|
-
_registerControllerRoutes() {
|
|
64
|
-
const defaultToJson = !!this.json;
|
|
65
|
-
const controllerName = this._getControllerName();
|
|
66
|
-
if (this.routes && this.routes.length) {
|
|
67
|
-
const localizedRoutes = this.routes.map(route => {
|
|
68
|
-
// TODO: throw an error if :controller is present in local routes
|
|
69
|
-
// TODO: also account for other malformed local routes
|
|
70
|
-
const routePrefix = `/${controllerName}/`;
|
|
71
|
-
const modifiedDestination = route[1][0] == '/'
|
|
72
|
-
? route[1]
|
|
73
|
-
: route[1].startsWith('./') ? route[1].replace('./', routePrefix) : routePrefix + route[1];
|
|
74
|
-
const modifiedOptions = {...route[2]};
|
|
75
|
-
// TODO: introduce a shorthand so action doesn't have to be redefined everywhere
|
|
76
|
-
if (!('json' in modifiedOptions)) modifiedOptions.json = defaultToJson;
|
|
77
|
-
if (!('action' in modifiedOptions)
|
|
78
|
-
&& !modifiedDestination.includes(":action")
|
|
79
|
-
&& !Object.values(modifiedOptions).includes('action')) {
|
|
80
|
-
const destinationParts = modifiedDestination.split("/");
|
|
81
|
-
modifiedOptions.action = destinationParts[destinationParts.length - 1];
|
|
82
|
-
}
|
|
83
|
-
modifiedOptions.controller = controllerName;
|
|
84
|
-
return [route[0], modifiedDestination, modifiedOptions];
|
|
85
|
-
});
|
|
86
|
-
router.addRoutes(localizedRoutes);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**** Network Methods *****/
|
|
91
|
-
/**
|
|
92
|
-
* The main entry function of the controller.
|
|
93
|
-
*
|
|
94
|
-
* @param {string} action The action called for this controller
|
|
95
|
-
* @param {object} params The parameter hash for this action
|
|
96
|
-
* @param {object} request The http request object from the http server
|
|
97
|
-
* @param {object} response The http response object from the http server
|
|
98
|
-
* @param {object} ws The WebSocket instance
|
|
99
|
-
*/
|
|
100
|
-
_do (action, params, request, response, ws) {
|
|
101
|
-
request.handled_by_controller = true;
|
|
102
|
-
var doAction = this[action];
|
|
103
|
-
console.log(this.constructor.name, 'doing', action);
|
|
104
|
-
// TODO: let express handle errors
|
|
105
|
-
if (typeof doAction != 'function') {
|
|
106
|
-
//return this['404'](params, request, response);
|
|
107
|
-
var error = new Error('Action Not Available: #' + action);
|
|
108
|
-
if (response) {
|
|
109
|
-
return response.status(404).send({
|
|
110
|
-
error: error.stack
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return ws.close(1003, "Action Not available: #" + action);
|
|
114
|
-
}
|
|
115
|
-
if (ws && !response) {
|
|
116
|
-
return doAction.call(this, params, ws, request);
|
|
117
|
-
}
|
|
118
|
-
// Override async with Controller definition.
|
|
119
|
-
if (doAction.async != undefined) params._async = !!doAction.async;
|
|
120
|
-
let actionResponse = doAction.call(this, params, request, response);
|
|
121
|
-
let controller = params._controller.toString();
|
|
122
|
-
//let viewAction = params._action ? params._action.toString() : "index";
|
|
123
|
-
let viewPath = `${controller}/${action}`;
|
|
124
|
-
function renderView(templateVars) {
|
|
125
|
-
if (response.headersSent) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (!!params._json) {
|
|
129
|
-
response.json(templateVars == undefined ? params : templateVars);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
response.render(
|
|
133
|
-
viewPath,
|
|
134
|
-
templateVars == undefined ? params : templateVars,
|
|
135
|
-
function(err, html) {
|
|
136
|
-
if (err && err.message.match(/Failed to lookup view/)) {
|
|
137
|
-
response.render(viewPath + ".ejs", params);
|
|
138
|
-
} else if (err) {
|
|
139
|
-
console.error(err);
|
|
140
|
-
response.sendStatus(400, err);
|
|
141
|
-
} else {
|
|
142
|
-
response.send(html);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
// Default to the jsx engine. If that doesn't work, try the configured view engine.
|
|
147
|
-
if (!response.headersSent
|
|
148
|
-
&& !params._async
|
|
149
|
-
&& actionResponse != DISABLE_AUTORENDER) {
|
|
150
|
-
if (actionResponse instanceof Promise) {
|
|
151
|
-
// TODO: add a timeout so an unresolved promise doesn't preserve the
|
|
152
|
-
// connection indefinitely.
|
|
153
|
-
actionResponse
|
|
154
|
-
.then(renderView)
|
|
155
|
-
.catch(error => {
|
|
156
|
-
// TODO: only do one of these
|
|
157
|
-
console.error(error);
|
|
158
|
-
console.log(error);
|
|
159
|
-
response.sendStatus(500, error);
|
|
160
|
-
});
|
|
161
|
-
} else renderView(actionResponse);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const error_codes = [404, 500];
|
|
167
|
-
error_codes.forEach((ec) => {
|
|
168
|
-
Controller.prototype[ec] = function (params, request, response) {
|
|
169
|
-
//this._render('404');
|
|
170
|
-
console.error(ec.toString() + ' error with params', params);
|
|
171
|
-
var error = params['error'] || {};
|
|
172
|
-
var message = error.message || 'Error';
|
|
173
|
-
var stack_trace = error.stack || error || '';
|
|
174
|
-
response.setHeader('content-type', 'text/plain');
|
|
175
|
-
response.statusCode = ec;
|
|
176
|
-
response.end(message + '\n\n' + stack_trace);
|
|
177
|
-
};
|
|
178
|
-
Controller.prototype[ec.toString()] = Controller.prototype[ec];
|
|
179
|
-
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
export default Controller;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export default class DbConnector {
|
|
2
|
-
async connect() {
|
|
3
|
-
throw 'DbConnector#connect not implemented'
|
|
4
|
-
}
|
|
5
|
-
generateModelSuperclass() {
|
|
6
|
-
throw 'GenerateModelSuperclass not implemented'
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
async afterInitialization() {
|
|
10
|
-
console.warn("DbConnector#afterInitialization not implemented");
|
|
11
|
-
}
|
|
12
|
-
}
|