mythix 1.2.0 → 2.0.2
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 +6 -13
- package/src/application.js +5 -2
- package/src/cli/migrations/makemigrations-command.js +1 -2
- package/src/cli/migrations/migrate-command.js +4 -4
- 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
|
@@ -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
|
};
|
package/src/models/model.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const { Model: _Model } = require('mythix-orm');
|
|
4
|
+
|
|
5
|
+
class Model extends _Model {
|
|
6
|
+
static getModel(modelName) {
|
|
7
|
+
if (modelName) {
|
|
8
|
+
let connection = this.getConnection();
|
|
9
|
+
return connection.getModel(modelName);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getModel(modelName) {
|
|
16
|
+
return this.constructor.getModel(modelName);
|
|
17
|
+
}
|
|
5
18
|
|
|
6
|
-
class Model extends Sequelize.Model {
|
|
7
19
|
getApplication() {
|
|
8
20
|
return this.constructor.getApplication();
|
|
9
21
|
}
|
|
@@ -13,21 +25,25 @@ class Model extends Sequelize.Model {
|
|
|
13
25
|
return application.getLogger();
|
|
14
26
|
}
|
|
15
27
|
|
|
16
|
-
getDBConnection() {
|
|
28
|
+
getDBConnection(connection) {
|
|
29
|
+
if (connection)
|
|
30
|
+
return connection;
|
|
31
|
+
|
|
17
32
|
let application = this.getApplication();
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
if (!application)
|
|
34
|
+
return null;
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
36
|
+
if (typeof application.getDBConnection === 'function')
|
|
37
|
+
return application.getDBConnection();
|
|
24
38
|
|
|
25
|
-
|
|
26
|
-
return this.constructor.getPrimaryKeyFieldName();
|
|
39
|
+
return null;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
getConnection(connection) {
|
|
43
|
+
if (connection)
|
|
44
|
+
return connection;
|
|
45
|
+
|
|
46
|
+
return this.getDBConnection();
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
overrideMethod(name, newMethod) {
|
|
@@ -60,143 +76,6 @@ class Model extends Sequelize.Model {
|
|
|
60
76
|
this.overrideMethod(name, method);
|
|
61
77
|
}
|
|
62
78
|
}
|
|
63
|
-
|
|
64
|
-
static prepareWhereStatement(conditions) {
|
|
65
|
-
if (Nife.isEmpty(conditions))
|
|
66
|
-
return undefined;
|
|
67
|
-
|
|
68
|
-
if (conditions._mythixQuery)
|
|
69
|
-
return conditions;
|
|
70
|
-
|
|
71
|
-
const Ops = Sequelize.Op;
|
|
72
|
-
let finalQuery = {};
|
|
73
|
-
let keys = Object.keys(conditions).concat(Object.getOwnPropertySymbols(conditions));
|
|
74
|
-
|
|
75
|
-
for (let i = 0, il = keys.length; i < il; i++) {
|
|
76
|
-
let key = keys[i];
|
|
77
|
-
let value = conditions[key];
|
|
78
|
-
|
|
79
|
-
if (value === undefined)
|
|
80
|
-
continue;
|
|
81
|
-
|
|
82
|
-
let name = key;
|
|
83
|
-
let invert = false;
|
|
84
|
-
|
|
85
|
-
if (typeof name === 'string' && name.charAt(0) === '!') {
|
|
86
|
-
name = name.substring(1);
|
|
87
|
-
invert = true;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (typeof key === 'symbol') {
|
|
91
|
-
finalQuery[key] = value;
|
|
92
|
-
} else if (value === null) {
|
|
93
|
-
finalQuery[name] = (invert) ? { [Ops.not]: value } : { [Ops.is]: value };
|
|
94
|
-
} else if (Nife.instanceOf(value, 'number', 'string', 'boolean', 'bigint')) {
|
|
95
|
-
finalQuery[name] = (invert) ? { [Ops.ne]: value } : { [Ops.eq]: value };
|
|
96
|
-
} else if (Nife.instanceOf(value, 'array') && Nife.isNotEmpty(value)) {
|
|
97
|
-
finalQuery[name] = (invert) ? { [Ops.not]: { [Ops.in]: value } } : { [Ops.in]: value };
|
|
98
|
-
} else if (Nife.isNotEmpty(value)) {
|
|
99
|
-
if (invert)
|
|
100
|
-
throw new Error(`Model.prepareWhereStatement: Attempted to invert a custom matcher "${name}"`);
|
|
101
|
-
|
|
102
|
-
finalQuery[name] = value;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (Nife.isEmpty(finalQuery))
|
|
107
|
-
return;
|
|
108
|
-
|
|
109
|
-
Object.defineProperties(finalQuery, {
|
|
110
|
-
'_mythixQuery': {
|
|
111
|
-
writable: false,
|
|
112
|
-
enumberable: false,
|
|
113
|
-
configurable: false,
|
|
114
|
-
value: true,
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
return finalQuery;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static getDefaultOrderBy() {
|
|
122
|
-
return [ this.getPrimaryKeyFieldName() ];
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
static prepareQueryOptions(conditions, _order) {
|
|
126
|
-
const Ops = Sequelize.Op;
|
|
127
|
-
let options;
|
|
128
|
-
let query;
|
|
129
|
-
|
|
130
|
-
if (conditions && Nife.isNotEmpty(conditions.where)) {
|
|
131
|
-
query = conditions.where;
|
|
132
|
-
options = Object.assign({}, conditions);
|
|
133
|
-
} else if (conditions && conditions.where !== null) {
|
|
134
|
-
query = this.prepareWhereStatement(conditions);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
let order = _order;
|
|
138
|
-
if (!options && Nife.instanceOf(order, 'object')) {
|
|
139
|
-
options = Object.assign({}, order);
|
|
140
|
-
order = options.order;
|
|
141
|
-
} else if (!options) {
|
|
142
|
-
options = {};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (Nife.isNotEmpty(query))
|
|
146
|
-
options.where = query;
|
|
147
|
-
|
|
148
|
-
if (!order && options.defaultOrder !== false) {
|
|
149
|
-
if (options.order) {
|
|
150
|
-
order = options.order;
|
|
151
|
-
} else {
|
|
152
|
-
if (typeof this.getDefaultOrderBy === 'function')
|
|
153
|
-
order = this.getDefaultOrderBy();
|
|
154
|
-
else
|
|
155
|
-
order = [ this.getPrimaryKeyFieldName() ];
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
options.order = order;
|
|
160
|
-
if (!Object.prototype.hasOwnProperty.call(options, 'distinct'))
|
|
161
|
-
options.distinct = true;
|
|
162
|
-
|
|
163
|
-
// If no "where" clause was specified, then grab everything
|
|
164
|
-
if (!options.where)
|
|
165
|
-
options.where = { [ this.getPrimaryKeyFieldName() ]: { [Ops.not]: null } };
|
|
166
|
-
|
|
167
|
-
if (options.debug)
|
|
168
|
-
console.log('QUERY OPTIONS: ', options);
|
|
169
|
-
|
|
170
|
-
return options;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
static where(conditions) {
|
|
174
|
-
return this.prepareWhereStatement(conditions);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
static async rowCount(conditions, options) {
|
|
178
|
-
return await this.count(this.prepareQueryOptions(conditions, options));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
static async bulkUpdate(attrs, conditions) {
|
|
182
|
-
return await this.update(attrs, this.prepareQueryOptions(conditions, { distinct: false }));
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
static async all(conditions, order) {
|
|
186
|
-
return await this.findAll(this.prepareQueryOptions(conditions, order));
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
static async first(conditions, order) {
|
|
190
|
-
return await this.findOne(this.prepareQueryOptions(conditions, order));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
static async last(conditions, _order) {
|
|
194
|
-
let order = _order;
|
|
195
|
-
if (!order)
|
|
196
|
-
order = [ [ 'createdAt', 'DESC' ] ];
|
|
197
|
-
|
|
198
|
-
return await this.first(conditions, order);
|
|
199
|
-
}
|
|
200
79
|
}
|
|
201
80
|
|
|
202
81
|
module.exports = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Nife
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
3
|
+
const Nife = require('nife');
|
|
4
|
+
const { BaseModule } = require('../modules/base-module');
|
|
5
|
+
const { ConnectionBase } = require('mythix-orm');
|
|
6
6
|
|
|
7
7
|
class DatabaseModule extends BaseModule {
|
|
8
8
|
static getModuleName() {
|
|
@@ -97,31 +97,36 @@ class DatabaseModule extends BaseModule {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
async connectToDatabase(databaseConfig) {
|
|
100
|
-
if (!databaseConfig)
|
|
101
|
-
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
100
|
+
if (!databaseConfig)
|
|
101
|
+
throw new Error('DatabaseModule::connectToDatabase: Database connection options not defined.');
|
|
104
102
|
|
|
105
|
-
let sequelize = new Sequelize(databaseConfig);
|
|
106
103
|
let dbConnectionString;
|
|
107
|
-
|
|
108
104
|
if (Nife.instanceOf(databaseConfig, 'string'))
|
|
109
105
|
dbConnectionString = databaseConfig;
|
|
110
106
|
else
|
|
111
107
|
dbConnectionString = `${databaseConfig.dialect}://${databaseConfig.host}:${databaseConfig.port || '<default port>'}/${databaseConfig.database}`;
|
|
112
108
|
|
|
113
109
|
try {
|
|
114
|
-
|
|
110
|
+
let app = this.getApplication();
|
|
111
|
+
if (typeof app.createDatabaseConnection !== 'function')
|
|
112
|
+
throw new Error('DatabaseModule::connectToDatabase: You must define a "createDatabaseConnection" method on your Application class.');
|
|
115
113
|
|
|
116
|
-
|
|
114
|
+
let connection = await app.createDatabaseConnection(databaseConfig);
|
|
115
|
+
if (!connection)
|
|
116
|
+
throw new Error('DatabaseModule::connectToDatabase: Application::createDatabaseConnection must return a connection.');
|
|
117
|
+
|
|
118
|
+
if (!(connection instanceof ConnectionBase) && typeof connection === 'function' && connection.prototype instanceof ConnectionBase) {
|
|
119
|
+
const ConnectionKlass = connection;
|
|
120
|
+
connection = new ConnectionKlass(databaseConfig);
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
await connection.start();
|
|
123
|
+
} else if (!connection.isStarted()) {
|
|
124
|
+
await connection.start();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.getLogger().info(`Connection to ${dbConnectionString} has been established successfully!`);
|
|
123
128
|
|
|
124
|
-
return
|
|
129
|
+
return connection;
|
|
125
130
|
} catch (error) {
|
|
126
131
|
this.getLogger().error(`Unable to connect to database ${dbConnectionString}:`, error);
|
|
127
132
|
throw error;
|
|
@@ -146,7 +151,7 @@ class DatabaseModule extends BaseModule {
|
|
|
146
151
|
return;
|
|
147
152
|
|
|
148
153
|
this.getLogger().info('Closing database connections...');
|
|
149
|
-
await this.connection.
|
|
154
|
+
await this.connection.stop();
|
|
150
155
|
this.getLogger().info('All database connections closed successfully!');
|
|
151
156
|
}
|
|
152
157
|
}
|