Skip to content

Commit 344e984

Browse files
committed
Updated README with info about schema validation.
1 parent 158b088 commit 344e984

File tree

4 files changed

+61
-56
lines changed

4 files changed

+61
-56
lines changed

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This is an opinionated base [Sails v1](https://sailsjs.com) application, using W
88
+ Setup for Webpack auto-reload dev server.
99
+ Setup so Sails will serve Webpack-built bundles as separate apps (so, a marketing site, and an admin site can live side-by-side).
1010
+ Includes [react-bootstrap](https://www.npmjs.com/package/react-bootstrap) to make using Bootstrap styles / features with React easier.
11+
+ Schema validation and enforcement for `PRODUCTION`. This repo is setup for `MySQL`. If you plan to use a different datastore, you will want to modify [`config/bootstrap.js`](config/bootstrap.js) (not to be confused with the CSS framework, this config file is what Sails runs just before finally "lifting". See [Sails documentation](https://sailsjs.com/config/bootstrap) about the `config/bootstrap.js` file.)
1112

1213
## How to Use
1314
This repo is not installable via `npm`. It should be forked to help kick-start projects.
@@ -43,14 +44,29 @@ The webpack configuration can be found in the `config/webpack` folder. The major
4344
## Building with React
4445
React source files live in the `assets/src` folder. It is structured in such a way, where the `index.jsx` is really only used for local development (to help Webpack serve up the correct "app"). Then, there are the individual "apps", [main](assets/src/main.jsx) and [admin](assets/src/admin.jsx). These files are used as Webpack "[entry points](https://webpack.js.org/concepts/entry-points/)", to create 2 separate application bundles.
4546

46-
In a remote environment, Sails will look at the first subdirectory requested, and use that to determine which `index.html` file it needs to actually return. So, in this case, the "main" application will get built in `.tmp/public/main`, where the CSS is `.tmp/public/main/bundle.css`, the JavaScript is `.tmp/public/main/bundle.js`, and the HTML is `.tmp/public/main/index.html`. To view the main application, one would just go to `http://mydomain/` which gets redirected to `/main` (because need to know what application we are using), and now Sails will serve the `main` application. Where as, if one were to go to `http://mydomain/admin`, Sails would now serve the `admin` application bundle.
47+
In a remote environment, Sails will look at the first subdirectory requested, and use that to determine which `index.html` file it needs to actually return. So, in this case, the "main" application will get built in `.tmp/public/main`, where the CSS is `.tmp/public/main/bundle.css`, the JavaScript is `.tmp/public/main/bundle.js`, and the HTML is `.tmp/public/main/index.html`. To view the main application, one would just go to `http://mydomain/` which gets redirected to `/main` (because we need to know what application we are using, we need a subdirectory), and now Sails will serve the `main` application. Whereas, if one were to go to `http://mydomain/admin`, Sails would now serve the `admin` application bundle (aka `.tmp/public/admin/index.html`).
4748

48-
### Links
49+
## Schema Validation and Enforcement
50+
Inside [`config/bootstrap.js`](config/bootstrap.js) is a bit of logic (**HEAVILY ROOTED IN NATIVE `MySQL` QUERIES**), which validates column types in the `PRODUCTION` database (aka `sails.config.models.migrate === 'safe'`), then will validate foreign key indexes. If there are too many columns, or there is a missing index, or incorrect column type, the logic will `console.error` any issues, then `process.exit(1)` (kill) the Sails server. The idea here, is that if anything is out of alignment, Sails will fail to lift, which will mean failure to deploy on AWS.
51+
52+
### If you do not want schema validation
53+
... then replace the contents of `config/bootstrap.js` with the following:
54+
55+
```javascript
56+
module.exports.bootstrap = function(next) {
57+
// You must call the callback function, or Sails will fail to lift!
58+
next();
59+
};
60+
```
61+
62+
### Useful Links
4963

5064
+ [Sails Framework Documentation](https://sailsjs.com/get-started)
5165
+ [Sails Deployment Tips](https://sailsjs.com/documentation/concepts/deployment)
5266
+ [Sails Community Support Options](https://sailsjs.com/support)
5367
+ [Sails Professional / Enterprise Options](https://sailsjs.com/enterprise)
68+
+ [`react-bootstrap` Documentation](https://react-bootstrap.netlify.app/)
69+
+ [Webpack Documentation](https://webpack.js.org/)
5470

5571

5672
### Version info

api/helpers/get-error-messages.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@ module.exports = {
2020
if (err.invalidAttributes && err.Errors) {
2121
err = _.merge({}, err.invalidAttributes, err.Errors);
2222

23-
sails.helpers.objForEach(err, function(error){
24-
error.forEach(function(errMessage){
23+
_.forEach(err, (error) => {
24+
error.forEach((errMessage) => {
2525
errors.push(errMessage.message);
2626
});
2727
});
2828
} else if (err.invalidAttributes) {
29-
sails.helpers.objForEach(err.invalidAttributes, function(error){
30-
error.forEach(function(errMessage){
29+
_.forEach(err.invalidAttributes, (error) => {
30+
error.forEach((errMessage) => {
3131
errors.push(errMessage.message);
3232
});
3333
});
3434
} else if (err.Errors) {
35-
sails.helpers.objForEach(err.Errors, function(error){
36-
error.forEach(function(errMessage){
35+
_.forEach(err.Errors, (error) => {
36+
error.forEach((errMessage) => {
3737
errors.push(errMessage.message);
3838
});
3939
});

api/helpers/obj-for-each.js

Lines changed: 0 additions & 37 deletions
This file was deleted.

config/bootstrap.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,15 @@
99
* https://sailsjs.com/config/bootstrap
1010
*/
1111

12-
// Use this for headers: http://patorjk.com/software/taag/#p=display&c=c&f=ANSI%20Shadow&t=ZeroHour
13-
// Use this for subheaders: http://patorjk.com/software/taag/#p=display&c=c&f=Calvin%20S&t=ZeroHour
14-
15-
const fs = require('fs');
12+
// Use this for headers: http://patorjk.com/software/taag/#p=display&c=c&f=ANSI%20Shadow&t=Sails
13+
// Use this for subheaders: http://patorjk.com/software/taag/#p=display&c=c&f=Calvin%20S&t=Woot
1614

1715
module.exports.bootstrap = function(next) {
1816
// Check if we need to validate our schema
19-
if (sails.config.models.migrate === 'safe') { // aka PROD
17+
if (sails.config.models.migrate === 'safe') { // aka PRODUCTION
2018
let waitingToFinish = 0;
2119

22-
sails.helpers.objForEach(sails.models, (model, modelName) => {
20+
_.forEach(sails.models, (model, modelName) => {
2321
if (model.tableName !== 'archive') {
2422
waitingToFinish++;
2523

@@ -262,28 +260,39 @@ module.exports.bootstrap = function(next) {
262260
}, 20);
263261
})();
264262

265-
// something is taking WAY too long... assume the worst
263+
// something is taking WAY too long... assume the worst... BAIL!
266264
setTimeout(() => {
267265
if (waitingToFinish > 0) {
268266
console.error('The database schema does not appear to match the model definitions.');
269267
process.exit(1);
270268
}
271269
}, 5000);
272270
} else {
271+
/***
272+
* ┌┐┌┌─┐┌┬┐ ┌─┐┬─┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐┬┌─┐┌┐┌
273+
* ││││ │ │ ├─┘├┬┘│ │ │││ ││ │ ││ ││││
274+
* ┘└┘└─┘ ┴ ┴ ┴└─└─┘─┴┘└─┘└─┘ ┴ ┴└─┘┘└┘
275+
*/
276+
273277
// no safety requirement for database modifications, bypass safeties
274278
return next();
275279
}
276280

277281
function validateIndexes() {
278282
let waitingToFinish = 0;
279283

280-
sails.helpers.objForEach(sails.models, (model, modelName) => {
284+
_.forEach(sails.models, (model, modelName) => {
281285
if (model.tableName !== 'archive' && model.associations && model.associations.length) {
282286
model.associations.map((association) => {
283287
waitingToFinish++;
284288

285-
// check this isn't a collection
289+
// make sure this isn't a collection
286290
if (!association.collection) {
291+
/***
292+
* ┌─┐┌─┐┌┬┐ ┌─┐┌─┐┬─┐┌─┐┬┌─┐┌┐┌ ┬┌─┌─┐┬ ┬┌─┐
293+
* │ ┬├┤ │ ├┤ │ │├┬┘├┤ ││ ┬│││ ├┴┐├┤ └┬┘└─┐
294+
* └─┘└─┘ ┴ └ └─┘┴└─└─┘┴└─┘┘└┘ ┴ ┴└─┘ ┴ └─┘
295+
*/
287296
sails.sendNativeQuery(
288297
'SELECT * FROM `information_schema`.`KEY_COLUMN_USAGE` WHERE `TABLE_NAME` = \'' + model.tableName
289298
+ '\' AND `COLUMN_NAME` = \'' + association.alias
@@ -293,17 +302,34 @@ module.exports.bootstrap = function(next) {
293302
if (err) {
294303
console.error(err);
295304

296-
return console.error('I can\'t seem to read the required relationship data. HALTING!');
305+
console.error('I can\'t seem to read the required relationship data. HALTING!');
306+
307+
return process.exit(1);
297308
}
298309

299310
if (!foundKeys.rows[0] || !foundKeys.rows[0]['REFERENCED_COLUMN_NAME'] || foundKeys.rows[0]['REFERENCED_COLUMN_NAME'] !== sails.models[association.model].primaryKey) {
311+
/***
312+
* ┌┐┌┌─┐ ┬─┐┌─┐┬ ┌─┐┌┬┐┬┌─┐┌┐┌┌─┐┬ ┬┬┌─┐ ┌─┐┌─┐┬ ┬┌┐┌┌┬┐
313+
* ││││ │ ├┬┘├┤ │ ├─┤ │ ││ ││││└─┐├─┤│├─┘ ├┤ │ ││ ││││ ││
314+
* ┘└┘└─┘ ┴└─└─┘┴─┘┴ ┴ ┴ ┴└─┘┘└┘└─┘┴ ┴┴┴ └ └─┘└─┘┘└┘─┴┘
315+
*/
300316
console.error('Column "' + association.alias + '" for "' + modelName + '" does not have a relationship setup');
301317
} else {
318+
/***
319+
* ┬─┐┌─┐┬ ┌─┐┌┬┐┬┌─┐┌┐┌┌─┐┬ ┬┬┌─┐ ┌─┐┌─┐┬ ┬┌┐┌┌┬┐
320+
* ├┬┘├┤ │ ├─┤ │ ││ ││││└─┐├─┤│├─┘ ├┤ │ ││ ││││ ││
321+
* ┴└─└─┘┴─┘┴ ┴ ┴ ┴└─┘┘└┘└─┘┴ ┴┴┴ └ └─┘└─┘┘└┘─┴┘
322+
*/
302323
waitingToFinish--;
303324
}
304325
}
305326
);
306327
} else {
328+
/***
329+
* ┬ ┬┌─┐┌─┐ ┌─┐┌─┐┬ ┬ ┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
330+
* │││├─┤└─┐ │ │ ││ │ ├┤ │ │ ││ ││││
331+
* └┴┘┴ ┴└─┘ └─┘└─┘┴─┘┴─┘└─┘└─┘ ┴ ┴└─┘┘└┘
332+
*/
307333
waitingToFinish--;
308334
}
309335
});
@@ -322,7 +348,7 @@ module.exports.bootstrap = function(next) {
322348

323349
setTimeout(() => {
324350
if (waitingToFinish > 0) {
325-
console.error('The database schema is missing indexes.');
351+
console.error('The database schema is missing foreign key indexes.');
326352
process.exit(1);
327353
}
328354
}, 3000);

0 commit comments

Comments
 (0)