mythix 1.2.0 → 2.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/package.json +4 -12
- package/src/application.js +5 -2
- package/src/cli/migrations/makemigrations-command.js +1 -2
- package/src/cli/shell-command.js +0 -2
- package/src/controllers/controller-module.js +0 -9
- package/src/http-server/http-server.js +0 -5
- package/src/models/index.js +0 -4
- package/src/models/migration-model.js +2 -2
- package/src/models/model-module.js +19 -32
- package/src/models/model-utils.js +24 -374
- package/src/models/model.js +29 -150
- package/src/modules/database-module.js +23 -18
- package/src/tasks/task-module.js +1 -11
- package/src/tasks/task-utils.js +1 -2
- package/src/utils/test-utils.js +38 -50
- package/.eslintrc.js +0 -94
- package/.vscode/settings.json +0 -7
- package/spec/controllers/controller-utils-spec.js +0 -145
- package/spec/controllers/generate-client-api-interface-spec.js +0 -156
- package/spec/controllers/generateClientAPIInterface01.snapshot +0 -552
- package/spec/controllers/generateClientAPIInterface02.snapshot +0 -465
- package/spec/controllers/generateClientAPIInterface03.snapshot +0 -418
- package/spec/controllers/generateClientAPIInterface04.snapshot +0 -552
- package/spec/controllers/generateClientAPIInterface05.snapshot +0 -552
- package/spec/support/application.js +0 -50
- package/spec/support/config/index.js +0 -3
- package/spec/support/jasmine.json +0 -13
- package/spec/support/snapshots.js +0 -63
- package/spec/utils/crypto-utils-spec.js +0 -28
- package/spec/utils/file-utils-spec.js +0 -14
- package/spec/utils/mime-utils-spec.js +0 -175
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mythix",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Mythix is a NodeJS web-app framework",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,26 +19,18 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/th317erd/mythix#readme",
|
|
21
21
|
"devDependencies": {
|
|
22
|
+
"@spothero/eslint-plugin-spothero": "github:spothero/eslint-plugin-spothero",
|
|
23
|
+
"eslint": "^8.13.0",
|
|
22
24
|
"colors": "^1.4.0",
|
|
23
25
|
"diff": "^5.1.0",
|
|
24
26
|
"jasmine": "^4.0.2"
|
|
25
27
|
},
|
|
26
28
|
"dependencies": {
|
|
27
|
-
"@spothero/eslint-plugin-spothero": "github:spothero/eslint-plugin-spothero",
|
|
28
29
|
"chokidar": "^3.5.3",
|
|
29
|
-
"deep-diff": "^1.0.2",
|
|
30
|
-
"eslint": "^8.13.0",
|
|
31
30
|
"express": "^4.17.3",
|
|
32
31
|
"express-busboy": "^8.0.2",
|
|
33
32
|
"form-data": "^4.0.0",
|
|
34
|
-
"inflection": "^1.13.2",
|
|
35
|
-
"lodash": "^4.17.21",
|
|
36
33
|
"nife": "^1.8.1",
|
|
37
|
-
"
|
|
38
|
-
"pg": "^8.7.3",
|
|
39
|
-
"pg-hstore": "^2.3.4",
|
|
40
|
-
"prompts": "^2.4.2",
|
|
41
|
-
"sequelize": "^6.18.0",
|
|
42
|
-
"sqlite3": "^5.0.9"
|
|
34
|
+
"prompts": "^2.4.2"
|
|
43
35
|
}
|
|
44
36
|
}
|
package/src/application.js
CHANGED
|
@@ -70,6 +70,7 @@ class Application extends EventEmitter {
|
|
|
70
70
|
let ROOT_PATH = (_opts && _opts.rootPath) ? _opts.rootPath : Path.resolve(__dirname);
|
|
71
71
|
|
|
72
72
|
let opts = Nife.extend(true, {
|
|
73
|
+
environment: (process.env.NODE_ENV || 'development'),
|
|
73
74
|
appName: this.constructor.APP_NAME,
|
|
74
75
|
rootPath: ROOT_PATH,
|
|
75
76
|
configPath: Path.resolve(ROOT_PATH, 'config'),
|
|
@@ -81,7 +82,7 @@ class Application extends EventEmitter {
|
|
|
81
82
|
commandsPath: Path.resolve(ROOT_PATH, 'commands'),
|
|
82
83
|
tasksPath: Path.resolve(ROOT_PATH, 'tasks'),
|
|
83
84
|
modules: this.constructor.getDefaultModules(),
|
|
84
|
-
autoReload: (process.env.NODE_ENV || 'development') === 'development',
|
|
85
|
+
autoReload: ((_opts && _opts.environment) || process.env.NODE_ENV || 'development') === 'development',
|
|
85
86
|
exitOnShutdown: null,
|
|
86
87
|
runTasks: true,
|
|
87
88
|
testMode: false,
|
|
@@ -229,8 +230,10 @@ class Application extends EventEmitter {
|
|
|
229
230
|
|
|
230
231
|
loadConfig(configPath) {
|
|
231
232
|
try {
|
|
233
|
+
let appOptions = this.getOptions();
|
|
234
|
+
|
|
232
235
|
const config = require(configPath);
|
|
233
|
-
return wrapConfig(config);
|
|
236
|
+
return wrapConfig(Object.assign({}, config || {}, { environment: (appOptions.environment || config.environment || 'development')}));
|
|
234
237
|
} catch (error) {
|
|
235
238
|
this.getLogger().error(`Error while trying to load application configuration ${configPath}: `, error);
|
|
236
239
|
throw error;
|
|
@@ -138,7 +138,7 @@ module.exports = defineCommand('makemigrations', ({ Parent }) => {
|
|
|
138
138
|
|
|
139
139
|
// Now build all model information
|
|
140
140
|
connection.modelManager.forEachModel((Model) => {
|
|
141
|
-
let modelName = Model.
|
|
141
|
+
let modelName = Model.getModelName();
|
|
142
142
|
let tableName = Model.getTableName(options);
|
|
143
143
|
|
|
144
144
|
models.push({
|
|
@@ -799,7 +799,6 @@ module.exports = defineCommand('makemigrations', ({ Parent }) => {
|
|
|
799
799
|
|
|
800
800
|
let oldAssertTableHasColumn = queryInterface.assertTableHasColumn;
|
|
801
801
|
try {
|
|
802
|
-
// Tell Sequelize to shut up
|
|
803
802
|
queryInterface.assertTableHasColumn = async () => ({ [oldColumnName]: columnDefinition });
|
|
804
803
|
|
|
805
804
|
return await queryInterface.renameColumn(tableName, oldColumnName, newColumnName, options);
|
package/src/cli/shell-command.js
CHANGED
|
@@ -5,7 +5,6 @@ const OS = require('os');
|
|
|
5
5
|
const Path = require('path');
|
|
6
6
|
const REPL = require('repl');
|
|
7
7
|
const UUIDV4 = require('uuid').v4;
|
|
8
|
-
const { Sequelize } = require('sequelize');
|
|
9
8
|
const { defineCommand } = require('./cli-utils');
|
|
10
9
|
const {
|
|
11
10
|
HTTPInterface,
|
|
@@ -55,7 +54,6 @@ module.exports = defineCommand('shell', ({ Parent }) => {
|
|
|
55
54
|
interactiveShell.setupHistory(Path.join(OS.homedir(), `.${appName}-${environment}-history`), () => {});
|
|
56
55
|
|
|
57
56
|
interactiveShell.context.UUIDV4 = UUIDV4;
|
|
58
|
-
interactiveShell.context.Sequelize = Sequelize;
|
|
59
57
|
interactiveShell.context.connection = (typeof application.getDBConnection === 'function') ? application.getDBConnection() : null;
|
|
60
58
|
interactiveShell.context.application = application;
|
|
61
59
|
interactiveShell.context.Nife = Nife;
|
|
@@ -90,15 +90,6 @@ class ControllerModule extends BaseModule {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
Object.defineProperties(controllers, {
|
|
94
|
-
'_files': {
|
|
95
|
-
writable: true,
|
|
96
|
-
enumberable: false,
|
|
97
|
-
configurable: true,
|
|
98
|
-
value: controllerFiles,
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
|
|
102
93
|
return controllers;
|
|
103
94
|
}
|
|
104
95
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
/* global process */
|
|
4
4
|
|
|
5
|
-
const OS = require('os');
|
|
6
5
|
const Path = require('path');
|
|
7
6
|
const FileSystem = require('fs');
|
|
8
7
|
const HTTP = require('http');
|
|
@@ -10,7 +9,6 @@ const HTTPS = require('https');
|
|
|
10
9
|
const Nife = require('nife');
|
|
11
10
|
const Express = require('express');
|
|
12
11
|
const ExpressBusBoy = require('express-busboy');
|
|
13
|
-
const Sequelize = require('sequelize');
|
|
14
12
|
|
|
15
13
|
const {
|
|
16
14
|
HTTPBaseError,
|
|
@@ -132,9 +130,6 @@ class HTTPServer {
|
|
|
132
130
|
if (!logger)
|
|
133
131
|
logger = request.mythixLogger = this.createRequestLogger(application, request);
|
|
134
132
|
|
|
135
|
-
if (!request.Sequelize)
|
|
136
|
-
request.Sequelize = Sequelize;
|
|
137
|
-
|
|
138
133
|
request.route = route;
|
|
139
134
|
request.params = params;
|
|
140
135
|
|
package/src/models/index.js
CHANGED
|
@@ -5,14 +5,10 @@ const { ModelModule } = require('./model-module');
|
|
|
5
5
|
|
|
6
6
|
const {
|
|
7
7
|
defineModel,
|
|
8
|
-
getModelPrimaryKeyField,
|
|
9
|
-
buildModelRelations,
|
|
10
8
|
} = require('./model-utils');
|
|
11
9
|
|
|
12
10
|
module.exports = {
|
|
13
|
-
buildModelRelations,
|
|
14
11
|
defineModel,
|
|
15
|
-
getModelPrimaryKeyField,
|
|
16
12
|
Model,
|
|
17
13
|
ModelModule,
|
|
18
14
|
};
|
|
@@ -4,11 +4,11 @@ const { defineModel } = require('./model-utils');
|
|
|
4
4
|
|
|
5
5
|
const ID_STRING_MAX_SIZE = 15;
|
|
6
6
|
|
|
7
|
-
module.exports = defineModel('Migration', ({ Parent,
|
|
7
|
+
module.exports = defineModel('Migration', ({ Parent, Types }) => {
|
|
8
8
|
return class Migration extends Parent {
|
|
9
9
|
static fields = {
|
|
10
10
|
id: {
|
|
11
|
-
type:
|
|
11
|
+
type: Types.STRING(ID_STRING_MAX_SIZE),
|
|
12
12
|
allowNull: false,
|
|
13
13
|
primaryKey: true,
|
|
14
14
|
index: true,
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
/* global __dirname */
|
|
4
4
|
|
|
5
|
-
const Path
|
|
6
|
-
const {
|
|
7
|
-
const { BaseModule } = require('../modules/base-module');
|
|
8
|
-
const { buildModelRelations } = require('./model-utils');
|
|
5
|
+
const Path = require('path');
|
|
6
|
+
const { BaseModule } = require('../modules/base-module');
|
|
9
7
|
const {
|
|
10
8
|
fileNameWithoutExtension,
|
|
11
9
|
walkDir,
|
|
@@ -28,15 +26,6 @@ class ModelModule extends BaseModule {
|
|
|
28
26
|
constructor(application) {
|
|
29
27
|
super(application);
|
|
30
28
|
|
|
31
|
-
Object.defineProperties(this, {
|
|
32
|
-
'models': {
|
|
33
|
-
writable: true,
|
|
34
|
-
enumerable: false,
|
|
35
|
-
configurable: true,
|
|
36
|
-
value: {},
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
29
|
// Inject methods into the application
|
|
41
30
|
Object.defineProperties(application, {
|
|
42
31
|
'getModel': {
|
|
@@ -59,8 +48,7 @@ class ModelModule extends BaseModule {
|
|
|
59
48
|
}
|
|
60
49
|
|
|
61
50
|
async fileWatcherHandler(options) {
|
|
62
|
-
|
|
63
|
-
this.models = models;
|
|
51
|
+
await this.loadModels(options.modelsPath);
|
|
64
52
|
}
|
|
65
53
|
|
|
66
54
|
getModelFilePaths(modelsPath) {
|
|
@@ -91,7 +79,7 @@ class ModelModule extends BaseModule {
|
|
|
91
79
|
let dbConfig = (typeof application.getDBConfig === 'function') ? application.getDBConfig() : null;
|
|
92
80
|
let modelFiles = this.getModelFilePaths(modelsPath);
|
|
93
81
|
let models = {};
|
|
94
|
-
let args = { application,
|
|
82
|
+
let args = { application, connection, dbConfig };
|
|
95
83
|
|
|
96
84
|
for (let i = 0, il = modelFiles.length; i < il; i++) {
|
|
97
85
|
let modelFile = modelFiles[i];
|
|
@@ -108,32 +96,31 @@ class ModelModule extends BaseModule {
|
|
|
108
96
|
}
|
|
109
97
|
}
|
|
110
98
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
Object.defineProperties(models, {
|
|
114
|
-
'_files': {
|
|
115
|
-
writable: true,
|
|
116
|
-
enumberable: false,
|
|
117
|
-
configurable: true,
|
|
118
|
-
value: modelFiles,
|
|
119
|
-
},
|
|
120
|
-
});
|
|
99
|
+
models = connection.registerModels(models);
|
|
121
100
|
|
|
122
101
|
return models;
|
|
123
102
|
}
|
|
124
103
|
|
|
125
|
-
getModel(
|
|
126
|
-
let
|
|
127
|
-
|
|
104
|
+
getModel(modelName) {
|
|
105
|
+
let application = this.getApplication();
|
|
106
|
+
let connection = (typeof application.getDBConnection === 'function') ? application.getDBConnection() : null;
|
|
107
|
+
if (!connection)
|
|
108
|
+
return;
|
|
109
|
+
|
|
110
|
+
return connection.getModel(modelName);
|
|
128
111
|
}
|
|
129
112
|
|
|
130
113
|
getModels() {
|
|
131
|
-
|
|
114
|
+
let application = this.getApplication();
|
|
115
|
+
let connection = (typeof application.getDBConnection === 'function') ? application.getDBConnection() : null;
|
|
116
|
+
if (!connection)
|
|
117
|
+
return;
|
|
118
|
+
|
|
119
|
+
return connection.getModels();
|
|
132
120
|
}
|
|
133
121
|
|
|
134
122
|
async start(options) {
|
|
135
|
-
|
|
136
|
-
this.models = models;
|
|
123
|
+
await this.loadModels(options.modelsPath);
|
|
137
124
|
}
|
|
138
125
|
|
|
139
126
|
async stop() {
|
|
@@ -1,398 +1,48 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Nife
|
|
4
|
-
const
|
|
5
|
-
const { Model }
|
|
6
|
-
|
|
7
|
-
const MILLISECONDS_PER_SECOND = 1000;
|
|
8
|
-
|
|
9
|
-
function relationHelper(modelName, type) {
|
|
10
|
-
return function(target, _options) {
|
|
11
|
-
const getName = () => {
|
|
12
|
-
if (options.name)
|
|
13
|
-
return options.name;
|
|
14
|
-
|
|
15
|
-
if (type.match(/many/i))
|
|
16
|
-
return Inflection.pluralize(target.toLowerCase());
|
|
17
|
-
else
|
|
18
|
-
return target.toLowerCase();
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// const getFieldName = () => {
|
|
22
|
-
// if (options.field)
|
|
23
|
-
// return options.field;
|
|
24
|
-
|
|
25
|
-
// if (type.match(/many/i))
|
|
26
|
-
// return Inflection.pluralize(target.toLowerCase());
|
|
27
|
-
// else
|
|
28
|
-
// return target.toLowerCase();
|
|
29
|
-
// };
|
|
30
|
-
|
|
31
|
-
let options = _options || {};
|
|
32
|
-
let defaultOnDeleteAction = 'RESTRICT';
|
|
33
|
-
if (options.allowNull)
|
|
34
|
-
defaultOnDeleteAction = 'SET NULL';
|
|
35
|
-
|
|
36
|
-
return Object.assign({}, options, {
|
|
37
|
-
type,
|
|
38
|
-
target,
|
|
39
|
-
onDelete: options.onDelete || defaultOnDeleteAction,
|
|
40
|
-
onUpdate: options.onUpdate || options.onDelete || defaultOnDeleteAction,
|
|
41
|
-
field: options.field,
|
|
42
|
-
name: getName(),
|
|
43
|
-
allowNull: (Object.prototype.hasOwnProperty.call(options, 'allowNull')) ? options.allowNull : true,
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const RELATION_HELPERS = [
|
|
49
|
-
'hasOne',
|
|
50
|
-
'belongsTo',
|
|
51
|
-
'hasMany',
|
|
52
|
-
'belongsToMany',
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
function getRelationHelpers(modelName) {
|
|
56
|
-
let obj = {};
|
|
57
|
-
|
|
58
|
-
for (let i = 0, il = RELATION_HELPERS.length; i < il; i++) {
|
|
59
|
-
let type = RELATION_HELPERS[i];
|
|
60
|
-
obj[type] = relationHelper(modelName, type);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return obj;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function preciseNow() {
|
|
67
|
-
let janFirst2022 = 1640995200000;
|
|
68
|
-
let now = Date.now() - janFirst2022;
|
|
69
|
-
let highResolutionNow = Nife.now();
|
|
70
|
-
let diff = Math.floor(highResolutionNow);
|
|
71
|
-
|
|
72
|
-
return Math.floor((now + (highResolutionNow - diff)) * MILLISECONDS_PER_SECOND);
|
|
73
|
-
}
|
|
3
|
+
const Nife = require('nife');
|
|
4
|
+
const { Types } = require('mythix-orm');
|
|
5
|
+
const { Model: ModelBase } = require('./model');
|
|
74
6
|
|
|
75
7
|
function defineModel(modelName, definer, _parent) {
|
|
76
|
-
function
|
|
77
|
-
const createAutoIncrementor = () => {
|
|
78
|
-
return () => preciseNow();
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
let fields = Klass.fields;
|
|
82
|
-
let fieldNames = Object.keys(fields);
|
|
83
|
-
let isSQLIte = !!('' + Nife.get(connection, 'options.dialect')).match(/sqlite/);
|
|
84
|
-
|
|
85
|
-
for (let i = 0, il = fieldNames.length; i < il; i++) {
|
|
86
|
-
let fieldName = fieldNames[i];
|
|
87
|
-
let field = fields[fieldName];
|
|
88
|
-
|
|
89
|
-
if (!field.field) {
|
|
90
|
-
let columnName = Nife.camelCaseToSnakeCase(fieldName);
|
|
91
|
-
field.field = columnName;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (!Object.prototype.hasOwnProperty.call(field, 'allowNull'))
|
|
95
|
-
field.allowNull = (field.primaryKey) ? false : true;
|
|
96
|
-
|
|
97
|
-
// If using SQLite, which doesn't support autoincrement
|
|
98
|
-
// on anything except the primary key, then create our
|
|
99
|
-
// own auto-incrementor for this field
|
|
100
|
-
if (field.autoIncrement && isSQLIte && !field.primaryKey) {
|
|
101
|
-
application.getLogger().warn(`!Warning!: Using an auto-increment field in SQLite on a non-primary-key column "${field.field}"! Be aware that this functionality is now emulated using high resolution timestamps. This won't work unless the column is a BIGINT. You may run into serious problems with this emulation!`);
|
|
102
|
-
field.defaultValue = createAutoIncrementor();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (field.type === DataTypes.BIGINT) {
|
|
106
|
-
if (!field.get) {
|
|
107
|
-
field.get = function(name) {
|
|
108
|
-
let value = this.getDataValue(name);
|
|
109
|
-
if (value == null)
|
|
110
|
-
return null;
|
|
111
|
-
|
|
112
|
-
return BigInt(value);
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!field.set) {
|
|
117
|
-
field.set = function(_value, name) {
|
|
118
|
-
let value = _value;
|
|
119
|
-
if (value == null)
|
|
120
|
-
value = null;
|
|
121
|
-
else
|
|
122
|
-
value = BigInt(value);
|
|
123
|
-
|
|
124
|
-
return this.setDataValue(name, value);
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return fields;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function cleanModelFields(Klass, connection) {
|
|
134
|
-
let finalFields = {};
|
|
135
|
-
let fields = Klass.fields;
|
|
136
|
-
let fieldNames = Object.keys(fields);
|
|
137
|
-
let isSQLIte = !!('' + Nife.get(connection, 'options.dialect')).match(/sqlite/);
|
|
138
|
-
|
|
139
|
-
for (let i = 0, il = fieldNames.length; i < il; i++) {
|
|
140
|
-
let fieldName = fieldNames[i];
|
|
141
|
-
let field = fields[fieldName];
|
|
142
|
-
let newField = Nife.extend(Nife.extend.FILTER, (key) => {
|
|
143
|
-
if (key.match(/^(index)$/))
|
|
144
|
-
return false;
|
|
145
|
-
|
|
146
|
-
// Strip "autoIncrement" if this is not the primary key
|
|
147
|
-
// and we are using sqlite for our dialect
|
|
148
|
-
if (key === 'autoIncrement' && isSQLIte && !field.primaryKey)
|
|
149
|
-
return false;
|
|
150
|
-
|
|
151
|
-
return true;
|
|
152
|
-
}, {}, field);
|
|
153
|
-
|
|
154
|
-
finalFields[fieldName] = newField;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return finalFields;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function generateIndexes(Klass) {
|
|
161
|
-
let finalIndexes = [];
|
|
162
|
-
let fields = Klass.fields;
|
|
163
|
-
let fieldNames = Object.keys(fields);
|
|
164
|
-
|
|
165
|
-
for (let i = 0, il = fieldNames.length; i < il; i++) {
|
|
166
|
-
let fieldName = fieldNames[i];
|
|
167
|
-
let field = fields[fieldName];
|
|
168
|
-
|
|
169
|
-
if (field.index) {
|
|
170
|
-
if (field.index === 'unique') {
|
|
171
|
-
finalIndexes.push({
|
|
172
|
-
unique: true,
|
|
173
|
-
fields: [ field.field ],
|
|
174
|
-
});
|
|
175
|
-
} else {
|
|
176
|
-
finalIndexes.push({
|
|
177
|
-
unique: false,
|
|
178
|
-
fields: [ field.field ],
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
finalIndexes = finalIndexes.concat(Klass.indexes || [], [
|
|
185
|
-
{
|
|
186
|
-
unique: false,
|
|
187
|
-
fields: [ 'created_at' ],
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
unique: false,
|
|
191
|
-
fields: [ 'updated_at' ],
|
|
192
|
-
},
|
|
193
|
-
]);
|
|
194
|
-
|
|
195
|
-
return finalIndexes;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return function({ application, Sequelize, connection }) {
|
|
8
|
+
return function({ application, connection }) {
|
|
199
9
|
let definerArgs = {
|
|
200
|
-
Parent: (_parent) ? _parent :
|
|
201
|
-
|
|
202
|
-
Relation: getRelationHelpers(modelName),
|
|
203
|
-
Sequelize,
|
|
10
|
+
Parent: (_parent) ? _parent : ModelBase,
|
|
11
|
+
Types,
|
|
204
12
|
connection,
|
|
205
13
|
modelName,
|
|
206
14
|
application,
|
|
207
15
|
};
|
|
208
16
|
|
|
209
|
-
let
|
|
210
|
-
|
|
211
|
-
Klass.customName = modelName;
|
|
212
|
-
|
|
213
|
-
if (typeof Klass.onModelClassCreate === 'function')
|
|
214
|
-
Klass = Klass.onModelClassCreate(Klass, definerArgs);
|
|
17
|
+
let Model = definer(definerArgs);
|
|
215
18
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Klass.pluralName = pluralName;
|
|
19
|
+
if (typeof Model.onModelClassCreate === 'function')
|
|
20
|
+
Model = Model.onModelClassCreate(Model, definerArgs);
|
|
219
21
|
|
|
220
|
-
|
|
22
|
+
let tableName = Model.getTableName();
|
|
23
|
+
let tablePrefix = application.getDBTablePrefix();
|
|
221
24
|
|
|
222
|
-
|
|
25
|
+
if (tablePrefix)
|
|
26
|
+
tableName = (`${tablePrefix}${tableName}`);
|
|
223
27
|
|
|
224
|
-
|
|
28
|
+
Model.getTableName = () => tableName;
|
|
29
|
+
Model.getModelName = () => modelName;
|
|
30
|
+
Model.getApplication = () => application;
|
|
31
|
+
Model.getLogger = () => application.getLogger();
|
|
32
|
+
Model._getConnection = (_connection) => {
|
|
33
|
+
if (_connection)
|
|
34
|
+
return _connection;
|
|
225
35
|
|
|
226
|
-
|
|
227
|
-
let tableName = Klass.tableName;
|
|
228
|
-
|
|
229
|
-
if (!tableName)
|
|
230
|
-
tableName = (`${Nife.get(applicationOptions, 'database.tablePrefix', '')}${Nife.camelCaseToSnakeCase(pluralName)}`).toLowerCase();
|
|
231
|
-
|
|
232
|
-
// Sequelize bullshit...
|
|
233
|
-
// Sequelize has undocumented static hook methods
|
|
234
|
-
// that don't align with their own hook system.
|
|
235
|
-
// Who knows what they are for, as they aren't
|
|
236
|
-
// documented, nor even called when statically
|
|
237
|
-
// defined on a model class... but... there you
|
|
238
|
-
// have it, stupid undocumented bullshit (again)
|
|
239
|
-
// that is breaking the world.
|
|
240
|
-
// So we must first check that each method isn't
|
|
241
|
-
// Sequelize bullshit, otherwise we get errors.
|
|
242
|
-
let staticHooks = {
|
|
243
|
-
beforeBulkCreate: Klass.beforeBulkCreate,
|
|
244
|
-
beforeBulkDestroy: Klass.beforeBulkDestroy,
|
|
245
|
-
beforeBulkUpdate: Klass.beforeBulkUpdate,
|
|
246
|
-
beforeValidate: Klass.beforeValidate,
|
|
247
|
-
afterValidate: Klass.afterValidate,
|
|
248
|
-
validationFailed: Klass.validationFailed,
|
|
249
|
-
beforeCreate: Klass.beforeCreate,
|
|
250
|
-
beforeDestroy: Klass.beforeDestroy,
|
|
251
|
-
beforeUpdate: Klass.beforeUpdate,
|
|
252
|
-
beforeSave: Klass.beforeSave,
|
|
253
|
-
beforeUpsert: Klass.beforeUpsert,
|
|
254
|
-
afterCreate: Klass.afterCreate,
|
|
255
|
-
afterDestroy: Klass.afterDestroy,
|
|
256
|
-
afterUpdate: Klass.afterUpdate,
|
|
257
|
-
afterSave: Klass.afterSave,
|
|
258
|
-
afterUpsert: Klass.afterUpsert,
|
|
259
|
-
afterBulkCreate: Klass.afterBulkCreate,
|
|
260
|
-
afterBulkDestroy: Klass.afterBulkDestroy,
|
|
261
|
-
afterBulkUpdate: Klass.afterBulkUpdate,
|
|
36
|
+
return connection;
|
|
262
37
|
};
|
|
263
|
-
let hookNames = Object.keys(staticHooks);
|
|
264
|
-
let hooks = {};
|
|
265
|
-
for (let i = 0, il = hookNames.length; i < il; i++) {
|
|
266
|
-
let hookName = hookNames[i];
|
|
267
|
-
let hookFunc = staticHooks[hookName];
|
|
268
|
-
if (typeof hookFunc !== 'function')
|
|
269
|
-
continue;
|
|
270
|
-
|
|
271
|
-
// Are you mysterious Sequelize stupidity?
|
|
272
|
-
// If so, skip the bullshit please.
|
|
273
|
-
if (hookFunc === Sequelize.Model[hookName])
|
|
274
|
-
continue;
|
|
275
38
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
Klass.init(Klass.fields, {
|
|
280
|
-
underscored: true,
|
|
281
|
-
freezeTableName: true,
|
|
282
|
-
sequelize: connection,
|
|
283
|
-
tableName,
|
|
284
|
-
modelName,
|
|
285
|
-
indexes,
|
|
286
|
-
name: {
|
|
287
|
-
singular: modelName.toLowerCase(),
|
|
288
|
-
plural: Inflection.pluralize(modelName.toLowerCase()),
|
|
289
|
-
},
|
|
290
|
-
hooks,
|
|
291
|
-
});
|
|
39
|
+
if (typeof Model.onModelClassFinalized === 'function')
|
|
40
|
+
Model = Model.onModelClassFinalized(Model, definerArgs);
|
|
292
41
|
|
|
293
|
-
|
|
294
|
-
Klass.getLogger = () => application.getLogger();
|
|
295
|
-
Klass.getModelName = (function() {
|
|
296
|
-
return modelName;
|
|
297
|
-
}).bind(Klass);
|
|
298
|
-
|
|
299
|
-
Klass.prototype.getModelName = function() {
|
|
300
|
-
return modelName;
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
Klass.getPrimaryKeyField = getModelPrimaryKeyField.bind(this, Klass);
|
|
304
|
-
Klass.getPrimaryKeyFieldName = () => (getModelPrimaryKeyField(Klass).field);
|
|
305
|
-
|
|
306
|
-
if (typeof Klass.onModelClassFinalized === 'function')
|
|
307
|
-
Klass = Klass.onModelClassFinalized(Klass, definerArgs);
|
|
308
|
-
|
|
309
|
-
return { [modelName]: Klass };
|
|
42
|
+
return { [modelName]: Model };
|
|
310
43
|
};
|
|
311
44
|
}
|
|
312
45
|
|
|
313
|
-
function getModelPrimaryKeyField(Klass) {
|
|
314
|
-
let fields = Klass.fields;
|
|
315
|
-
let fieldNames = Object.keys(fields);
|
|
316
|
-
|
|
317
|
-
for (let i = 0, il = fieldNames.length; i < il; i++) {
|
|
318
|
-
let fieldName = fieldNames[i];
|
|
319
|
-
let field = fields[fieldName];
|
|
320
|
-
|
|
321
|
-
if (field.primaryKey)
|
|
322
|
-
return field;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function buildModelRelations(models) {
|
|
327
|
-
let modelNames = Object.keys(models);
|
|
328
|
-
for (let i = 0, il = modelNames.length; i < il; i++) {
|
|
329
|
-
let modelName = modelNames[i];
|
|
330
|
-
let model = models[modelName];
|
|
331
|
-
let relations = model.relations;
|
|
332
|
-
|
|
333
|
-
if (!relations)
|
|
334
|
-
continue;
|
|
335
|
-
|
|
336
|
-
for (let j = 0, jl = relations.length; j < jl; j++) {
|
|
337
|
-
let relation = relations[j];
|
|
338
|
-
let type = relation.type;
|
|
339
|
-
let fieldName = Nife.camelCaseToSnakeCase(relation.field);
|
|
340
|
-
let targetModelName = relation.target;
|
|
341
|
-
let targetModel = models[targetModelName];
|
|
342
|
-
let belongsType = !!type.match(/^belongs/);
|
|
343
|
-
|
|
344
|
-
if (!targetModel)
|
|
345
|
-
throw new Error(`${modelName} relation error: target model ${targetModelName} not found`);
|
|
346
|
-
|
|
347
|
-
let primaryKeyField;
|
|
348
|
-
|
|
349
|
-
if (belongsType) {
|
|
350
|
-
primaryKeyField = getModelPrimaryKeyField(targetModel);
|
|
351
|
-
|
|
352
|
-
if (!primaryKeyField)
|
|
353
|
-
throw new Error(`${modelName} relation error: primary key for model ${targetModelName} not found`);
|
|
354
|
-
|
|
355
|
-
let pkFieldName = primaryKeyField.field;
|
|
356
|
-
if (pkFieldName === 'id')
|
|
357
|
-
pkFieldName = 'ID';
|
|
358
|
-
|
|
359
|
-
if (!fieldName)
|
|
360
|
-
fieldName = `${Nife.camelCaseToSnakeCase(targetModelName)}${Nife.snakeCaseToCamelCase(pkFieldName, true)}`;
|
|
361
|
-
} else {
|
|
362
|
-
primaryKeyField = getModelPrimaryKeyField(model);
|
|
363
|
-
|
|
364
|
-
if (!primaryKeyField)
|
|
365
|
-
throw new Error(`${modelName} relation error: primary key for model ${modelName} not found`);
|
|
366
|
-
|
|
367
|
-
let pkFieldName = primaryKeyField.field;
|
|
368
|
-
if (pkFieldName === 'id')
|
|
369
|
-
pkFieldName = 'ID';
|
|
370
|
-
|
|
371
|
-
if (!fieldName)
|
|
372
|
-
fieldName = `${Nife.camelCaseToSnakeCase(modelName)}${Nife.snakeCaseToCamelCase(pkFieldName, true)}`;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
let pkFieldCopy = Nife.extend(Nife.extend.FILTER, (key) => !key.match(/^(field|primaryKey)$/), {}, primaryKeyField);
|
|
376
|
-
|
|
377
|
-
// Build relation options for sequelize
|
|
378
|
-
let options = Object.assign({}, relation, {
|
|
379
|
-
onDelete: relation.onDelete,
|
|
380
|
-
onUpdate: relation.onUpdate,
|
|
381
|
-
allowNull: (relation.allowNull == null) ? true : relation.allowNull,
|
|
382
|
-
foreignKey: (relation.foreignKey) ? relation.foreignKey : Object.assign(pkFieldCopy, { name: fieldName, as: relation.name, field: Nife.camelCaseToSnakeCase(fieldName) }),
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
// Set relation on model
|
|
386
|
-
|
|
387
|
-
// console.log(`Creating model relation (${modelName} -> ${targetModelName}): `, type, options);
|
|
388
|
-
|
|
389
|
-
model[type](targetModel, options);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
46
|
module.exports = {
|
|
395
47
|
defineModel,
|
|
396
|
-
getModelPrimaryKeyField,
|
|
397
|
-
buildModelRelations,
|
|
398
48
|
};
|