@nocobase/database 0.7.0-alpha.82 → 0.7.1-alpha.4
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/lib/collection-importer.js +25 -42
- package/lib/collection.d.ts +6 -2
- package/lib/collection.js +37 -5
- package/lib/database.d.ts +21 -5
- package/lib/database.js +161 -49
- package/lib/fields/field.d.ts +4 -1
- package/lib/fields/field.js +117 -0
- package/lib/fields/formula-field.d.ts +19 -0
- package/lib/fields/formula-field.js +184 -0
- package/lib/fields/index.d.ts +3 -1
- package/lib/fields/index.js +13 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +14 -0
- package/lib/migration.d.ts +35 -0
- package/lib/migration.js +90 -0
- package/lib/mock-database.d.ts +1 -0
- package/lib/mock-database.js +2 -1
- package/lib/model-hook.d.ts +5 -5
- package/lib/model-hook.js +26 -18
- package/lib/options-parser.js +65 -43
- package/lib/relation-repository/relation-repository.js +11 -1
- package/lib/relation-repository/single-relation-repository.js +8 -1
- package/lib/repository.js +12 -4
- package/lib/update-associations.js +1 -1
- package/package.json +9 -4
- package/src/__tests__/collection.test.ts +27 -0
- package/src/__tests__/database.test.ts +47 -0
- package/src/__tests__/fields/formula-field.test.ts +69 -0
- package/src/__tests__/fixtures/migrations/m1.ts +7 -0
- package/src/__tests__/fixtures/migrations/m2.ts +7 -0
- package/src/__tests__/hooks/afterCreateWithAssociations.test.ts +33 -0
- package/src/__tests__/migrator.test.ts +70 -0
- package/src/__tests__/model-hook.test.ts +54 -0
- package/src/__tests__/option-parser.test.ts +10 -6
- package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +1 -1
- package/src/__tests__/sequelize-hooks.test.ts +69 -0
- package/src/__tests__/sort.test.ts +51 -0
- package/src/__tests__/update-associations.test.ts +3 -3
- package/src/collection-importer.ts +12 -20
- package/src/collection.ts +26 -2
- package/src/database.ts +112 -29
- package/src/fields/field.ts +88 -1
- package/src/fields/formula-field.ts +106 -0
- package/src/fields/index.ts +3 -0
- package/src/index.ts +1 -0
- package/src/migration.ts +76 -0
- package/src/mock-database.ts +1 -0
- package/src/model-hook.ts +25 -21
- package/src/options-parser.ts +13 -9
- package/src/relation-repository/multiple-relation-repository.ts +8 -2
- package/src/relation-repository/relation-repository.ts +1 -0
- package/src/relation-repository/single-relation-repository.ts +5 -1
- package/src/repository.ts +16 -4
- package/src/update-associations.ts +1 -1
package/lib/options-parser.js
CHANGED
|
@@ -19,18 +19,18 @@ var _filterParser = _interopRequireDefault(require("./filter-parser"));
|
|
|
19
19
|
|
|
20
20
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
21
21
|
|
|
22
|
-
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
23
|
-
|
|
24
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
25
|
-
|
|
26
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
27
|
-
|
|
28
22
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
29
23
|
|
|
30
24
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
31
25
|
|
|
32
26
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
33
27
|
|
|
28
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
29
|
+
|
|
30
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
31
|
+
|
|
32
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
33
|
+
|
|
34
34
|
const debug = require('debug')('noco-database');
|
|
35
35
|
|
|
36
36
|
class OptionsParser {
|
|
@@ -94,23 +94,45 @@ class OptionsParser {
|
|
|
94
94
|
sort = sort.split(',');
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
const orderParams =
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
const orderParams = [];
|
|
98
|
+
|
|
99
|
+
var _iterator = _createForOfIteratorHelper(sort),
|
|
100
|
+
_step;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
104
|
+
const sortKey = _step.value;
|
|
105
|
+
let direction = sortKey.startsWith('-') ? 'DESC' : 'ASC';
|
|
106
|
+
let sortField = sortKey.replace('-', '').split('.');
|
|
107
|
+
|
|
108
|
+
if (this.database.inDialect('postgres', 'sqlite')) {
|
|
109
|
+
direction = `${direction} NULLS LAST`;
|
|
110
|
+
} // handle sort by association
|
|
111
|
+
|
|
100
112
|
|
|
101
|
-
|
|
102
|
-
|
|
113
|
+
if (sortField.length > 1) {
|
|
114
|
+
let associationModel = this.model;
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
for (let i = 0; i < sortField.length - 1; i++) {
|
|
117
|
+
const associationKey = sortField[i];
|
|
118
|
+
sortField[i] = associationModel.associations[associationKey].target;
|
|
119
|
+
associationModel = sortField[i];
|
|
120
|
+
}
|
|
108
121
|
}
|
|
109
|
-
}
|
|
110
122
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
sortField.push(direction);
|
|
124
|
+
|
|
125
|
+
if (this.database.inDialect('mysql')) {
|
|
126
|
+
orderParams.push([_sequelize().Sequelize.fn('ISNULL', _sequelize().Sequelize.col(`${this.model.name}.${sortField[0]}`))]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
orderParams.push(sortField);
|
|
130
|
+
}
|
|
131
|
+
} catch (err) {
|
|
132
|
+
_iterator.e(err);
|
|
133
|
+
} finally {
|
|
134
|
+
_iterator.f();
|
|
135
|
+
}
|
|
114
136
|
|
|
115
137
|
if (orderParams.length > 0) {
|
|
116
138
|
return _objectSpread({
|
|
@@ -133,12 +155,12 @@ class OptionsParser {
|
|
|
133
155
|
|
|
134
156
|
if ((_this$options4 = this.options) === null || _this$options4 === void 0 ? void 0 : _this$options4.fields) {
|
|
135
157
|
// 将fields拆分为 attributes 和 appends
|
|
136
|
-
var
|
|
137
|
-
|
|
158
|
+
var _iterator2 = _createForOfIteratorHelper(this.options.fields),
|
|
159
|
+
_step2;
|
|
138
160
|
|
|
139
161
|
try {
|
|
140
|
-
for (
|
|
141
|
-
const field =
|
|
162
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
163
|
+
const field = _step2.value;
|
|
142
164
|
|
|
143
165
|
if (this.isAssociationPath(field)) {
|
|
144
166
|
// field is association field
|
|
@@ -150,19 +172,19 @@ class OptionsParser {
|
|
|
150
172
|
}
|
|
151
173
|
}
|
|
152
174
|
} catch (err) {
|
|
153
|
-
|
|
175
|
+
_iterator2.e(err);
|
|
154
176
|
} finally {
|
|
155
|
-
|
|
177
|
+
_iterator2.f();
|
|
156
178
|
}
|
|
157
179
|
}
|
|
158
180
|
|
|
159
181
|
if ((_this$options5 = this.options) === null || _this$options5 === void 0 ? void 0 : _this$options5.except) {
|
|
160
|
-
var
|
|
161
|
-
|
|
182
|
+
var _iterator3 = _createForOfIteratorHelper(this.options.except),
|
|
183
|
+
_step3;
|
|
162
184
|
|
|
163
185
|
try {
|
|
164
|
-
for (
|
|
165
|
-
const exceptKey =
|
|
186
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
187
|
+
const exceptKey = _step3.value;
|
|
166
188
|
|
|
167
189
|
if (this.isAssociationPath(exceptKey)) {
|
|
168
190
|
// except association field
|
|
@@ -174,9 +196,9 @@ class OptionsParser {
|
|
|
174
196
|
}
|
|
175
197
|
}
|
|
176
198
|
} catch (err) {
|
|
177
|
-
|
|
199
|
+
_iterator3.e(err);
|
|
178
200
|
} finally {
|
|
179
|
-
|
|
201
|
+
_iterator3.f();
|
|
180
202
|
}
|
|
181
203
|
}
|
|
182
204
|
|
|
@@ -218,18 +240,18 @@ class OptionsParser {
|
|
|
218
240
|
}
|
|
219
241
|
};
|
|
220
242
|
|
|
221
|
-
var
|
|
222
|
-
|
|
243
|
+
var _iterator4 = _createForOfIteratorHelper(except),
|
|
244
|
+
_step4;
|
|
223
245
|
|
|
224
246
|
try {
|
|
225
|
-
for (
|
|
226
|
-
const exceptKey =
|
|
247
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
248
|
+
const exceptKey = _step4.value;
|
|
227
249
|
setExcept(filterParams, exceptKey);
|
|
228
250
|
}
|
|
229
251
|
} catch (err) {
|
|
230
|
-
|
|
252
|
+
_iterator4.e(err);
|
|
231
253
|
} finally {
|
|
232
|
-
|
|
254
|
+
_iterator4.f();
|
|
233
255
|
}
|
|
234
256
|
|
|
235
257
|
return filterParams;
|
|
@@ -317,18 +339,18 @@ class OptionsParser {
|
|
|
317
339
|
}; // handle every appends
|
|
318
340
|
|
|
319
341
|
|
|
320
|
-
var
|
|
321
|
-
|
|
342
|
+
var _iterator5 = _createForOfIteratorHelper(appends),
|
|
343
|
+
_step5;
|
|
322
344
|
|
|
323
345
|
try {
|
|
324
|
-
for (
|
|
325
|
-
const append =
|
|
346
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
347
|
+
const append = _step5.value;
|
|
326
348
|
setInclude(this.model, filterParams, append);
|
|
327
349
|
}
|
|
328
350
|
} catch (err) {
|
|
329
|
-
|
|
351
|
+
_iterator5.e(err);
|
|
330
352
|
} finally {
|
|
331
|
-
|
|
353
|
+
_iterator5.f();
|
|
332
354
|
}
|
|
333
355
|
|
|
334
356
|
debug('filter params: %o', filterParams);
|
|
@@ -37,6 +37,14 @@ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try
|
|
|
37
37
|
|
|
38
38
|
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
39
39
|
|
|
40
|
+
var __decorate = void 0 && (void 0).__decorate || function (decorators, target, key, desc) {
|
|
41
|
+
var c = arguments.length,
|
|
42
|
+
r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
|
|
43
|
+
d;
|
|
44
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
45
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
46
|
+
};
|
|
47
|
+
|
|
40
48
|
const transaction = (0, _transactionDecorator.transactionWrapperBuilder)(function () {
|
|
41
49
|
return this.sourceCollection.model.sequelize.transaction();
|
|
42
50
|
});
|
|
@@ -157,4 +165,6 @@ class RelationRepository {
|
|
|
157
165
|
|
|
158
166
|
}
|
|
159
167
|
|
|
160
|
-
exports.RelationRepository = RelationRepository;
|
|
168
|
+
exports.RelationRepository = RelationRepository;
|
|
169
|
+
|
|
170
|
+
__decorate([transaction()], RelationRepository.prototype, "create", null);
|
|
@@ -86,7 +86,9 @@ class SingleRelationRepository extends _relationRepository.RelationRepository {
|
|
|
86
86
|
var _this4 = this;
|
|
87
87
|
|
|
88
88
|
return _asyncToGenerator(function* () {
|
|
89
|
-
return _this4.find(options)
|
|
89
|
+
return _this4.find(_objectSpread(_objectSpread({}, options), {}, {
|
|
90
|
+
filterByTk: null
|
|
91
|
+
}));
|
|
90
92
|
})();
|
|
91
93
|
}
|
|
92
94
|
|
|
@@ -113,6 +115,11 @@ class SingleRelationRepository extends _relationRepository.RelationRepository {
|
|
|
113
115
|
const target = yield _this6.find({
|
|
114
116
|
transaction
|
|
115
117
|
});
|
|
118
|
+
|
|
119
|
+
if (!target) {
|
|
120
|
+
throw new Error('The record does not exist');
|
|
121
|
+
}
|
|
122
|
+
|
|
116
123
|
yield (0, _updateAssociations.updateModelByValues)(target, options === null || options === void 0 ? void 0 : options.values, _objectSpread(_objectSpread({}, _lodash().default.omit(options, 'values')), {}, {
|
|
117
124
|
transaction
|
|
118
125
|
}));
|
package/lib/repository.js
CHANGED
|
@@ -257,8 +257,12 @@ class Repository {
|
|
|
257
257
|
}));
|
|
258
258
|
|
|
259
259
|
if (options.hooks !== false) {
|
|
260
|
-
yield _this5.database.emitAsync(`${_this5.collection.name}.afterCreateWithAssociations`, instance, options)
|
|
261
|
-
|
|
260
|
+
yield _this5.database.emitAsync(`${_this5.collection.name}.afterCreateWithAssociations`, instance, _objectSpread(_objectSpread({}, options), {}, {
|
|
261
|
+
transaction
|
|
262
|
+
}));
|
|
263
|
+
yield _this5.database.emitAsync(`${_this5.collection.name}.afterSaveWithAssociations`, instance, _objectSpread(_objectSpread({}, options), {}, {
|
|
264
|
+
transaction
|
|
265
|
+
}));
|
|
262
266
|
}
|
|
263
267
|
|
|
264
268
|
return instance;
|
|
@@ -349,8 +353,12 @@ class Repository {
|
|
|
349
353
|
try {
|
|
350
354
|
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
351
355
|
const instance = _step3.value;
|
|
352
|
-
yield _this7.database.emitAsync(`${_this7.collection.name}.afterUpdateWithAssociations`, instance, options)
|
|
353
|
-
|
|
356
|
+
yield _this7.database.emitAsync(`${_this7.collection.name}.afterUpdateWithAssociations`, instance, _objectSpread(_objectSpread({}, options), {}, {
|
|
357
|
+
transaction
|
|
358
|
+
}));
|
|
359
|
+
yield _this7.database.emitAsync(`${_this7.collection.name}.afterSaveWithAssociations`, instance, _objectSpread(_objectSpread({}, options), {}, {
|
|
360
|
+
transaction
|
|
361
|
+
}));
|
|
354
362
|
}
|
|
355
363
|
} catch (err) {
|
|
356
364
|
_iterator3.e(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/database",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1-alpha.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -12,17 +12,22 @@
|
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@nocobase/utils": "0.7.
|
|
15
|
+
"@nocobase/utils": "0.7.1-alpha.4",
|
|
16
16
|
"async-mutex": "^0.3.2",
|
|
17
17
|
"deepmerge": "^4.2.2",
|
|
18
18
|
"flat": "^5.0.2",
|
|
19
19
|
"glob": "^7.1.6",
|
|
20
|
-
"
|
|
20
|
+
"mathjs": "^10.6.1",
|
|
21
|
+
"sequelize": "^6.9.0",
|
|
22
|
+
"umzug": "^3.1.1"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/glob": "^7.2.0"
|
|
21
26
|
},
|
|
22
27
|
"repository": {
|
|
23
28
|
"type": "git",
|
|
24
29
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
25
30
|
"directory": "packages/database"
|
|
26
31
|
},
|
|
27
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "570d039f1904a041729c1967c0637b1109c278a8"
|
|
28
33
|
}
|
|
@@ -13,6 +13,33 @@ describe('collection', () => {
|
|
|
13
13
|
await db.close();
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
test('removeFromDb', async () => {
|
|
17
|
+
await db.clean({ drop: true });
|
|
18
|
+
const collection = db.collection({
|
|
19
|
+
name: 'test',
|
|
20
|
+
fields: [
|
|
21
|
+
{
|
|
22
|
+
type: 'string',
|
|
23
|
+
name: 'name',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
await db.sync();
|
|
28
|
+
|
|
29
|
+
const field = collection.getField('name');
|
|
30
|
+
const r1 = await field.existsInDb();
|
|
31
|
+
expect(r1).toBe(true);
|
|
32
|
+
await field.removeFromDb();
|
|
33
|
+
const r2 = await field.existsInDb();
|
|
34
|
+
expect(r2).toBe(false);
|
|
35
|
+
|
|
36
|
+
const r3 = await collection.existsInDb();
|
|
37
|
+
expect(r3).toBe(true);
|
|
38
|
+
await collection.removeFromDb();
|
|
39
|
+
const r4 = await collection.existsInDb();
|
|
40
|
+
expect(r4).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
16
43
|
test('collection disable authGenId', async () => {
|
|
17
44
|
const Test = db.collection({
|
|
18
45
|
name: 'test',
|
|
@@ -204,6 +204,53 @@ describe('database', () => {
|
|
|
204
204
|
expect(listener).toHaveBeenCalled();
|
|
205
205
|
});
|
|
206
206
|
|
|
207
|
+
test('off collection event', async () => {
|
|
208
|
+
const Post = db.collection({
|
|
209
|
+
name: 'posts',
|
|
210
|
+
fields: [{ type: 'string', name: 'title' }],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const postAfterCreateListener = jest.fn();
|
|
214
|
+
|
|
215
|
+
db.on('posts.afterCreate', postAfterCreateListener);
|
|
216
|
+
|
|
217
|
+
await db.sync();
|
|
218
|
+
|
|
219
|
+
db.off('posts.afterCreate', postAfterCreateListener);
|
|
220
|
+
|
|
221
|
+
await Post.repository.create({
|
|
222
|
+
values: {
|
|
223
|
+
title: 'test',
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
expect(postAfterCreateListener).toHaveBeenCalledTimes(0);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('off global event', async () => {
|
|
231
|
+
const Post = db.collection({
|
|
232
|
+
name: 'posts',
|
|
233
|
+
fields: [{ type: 'string', name: 'title' }],
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const postAfterCreateListener = jest.fn();
|
|
237
|
+
|
|
238
|
+
db.on('posts.afterCreate', postAfterCreateListener);
|
|
239
|
+
db.on('afterCreate', postAfterCreateListener);
|
|
240
|
+
|
|
241
|
+
await db.sync();
|
|
242
|
+
|
|
243
|
+
db.off('afterCreate', postAfterCreateListener);
|
|
244
|
+
|
|
245
|
+
await Post.repository.create({
|
|
246
|
+
values: {
|
|
247
|
+
title: 'test',
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(postAfterCreateListener).toHaveBeenCalledTimes(1);
|
|
252
|
+
});
|
|
253
|
+
|
|
207
254
|
test('custom model', async () => {
|
|
208
255
|
class CustomModel extends Model {
|
|
209
256
|
customMethod() {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { mockDatabase } from '..';
|
|
2
|
+
import { Database } from '../../database';
|
|
3
|
+
import { FormulaField } from '../../fields';
|
|
4
|
+
|
|
5
|
+
describe('formula field', () => {
|
|
6
|
+
let db: Database;
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
db = mockDatabase();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await db.close();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('add formula field with old table, already has data.', async () => {
|
|
17
|
+
const Test = db.collection({
|
|
18
|
+
name: 'tests',
|
|
19
|
+
fields: [
|
|
20
|
+
{ type: 'float', name: 'price' },
|
|
21
|
+
{ type: 'float', name: 'count' },
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await db.sync();
|
|
26
|
+
|
|
27
|
+
const test = await Test.model.create<any>({
|
|
28
|
+
price: '1.2',
|
|
29
|
+
count: '2',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const expression = 'price*count';
|
|
33
|
+
const field = Test.addField('sum', { type: 'formula', expression });
|
|
34
|
+
|
|
35
|
+
await field.sync({});
|
|
36
|
+
|
|
37
|
+
const updatedTest = await Test.model.findByPk(test.id);
|
|
38
|
+
const sum = updatedTest.get('sum');
|
|
39
|
+
|
|
40
|
+
const sumField = Test.getField<FormulaField>('sum');
|
|
41
|
+
expect(sum).toEqual(sumField.caculate(expression, updatedTest.toJSON()));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('auto set formula field with create or update data', async () => {
|
|
45
|
+
const expression = 'price*count';
|
|
46
|
+
const Test = db.collection({
|
|
47
|
+
name: 'tests',
|
|
48
|
+
fields: [
|
|
49
|
+
{ type: 'float', name: 'price' },
|
|
50
|
+
{ type: 'float', name: 'count' },
|
|
51
|
+
{ name: 'sum', type: 'formula', expression },
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await db.sync();
|
|
56
|
+
|
|
57
|
+
const test = await Test.model.create<any>({
|
|
58
|
+
price: '1.2',
|
|
59
|
+
count: '2',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const sumField = Test.getField<FormulaField>('sum');
|
|
63
|
+
expect(test.get('sum')).toEqual(sumField.caculate(expression, test.toJSON()));
|
|
64
|
+
|
|
65
|
+
test.set('count', '6');
|
|
66
|
+
await test.save();
|
|
67
|
+
expect(test.get('sum')).toEqual(sumField.caculate(expression, test.toJSON()));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { mockDatabase } from '../';
|
|
2
|
+
import { Database } from '../../database';
|
|
3
|
+
|
|
4
|
+
describe('afterCreateWithAssociations', () => {
|
|
5
|
+
let db: Database;
|
|
6
|
+
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
db = mockDatabase();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await db.close();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('case 1', async () => {
|
|
16
|
+
db.collection({
|
|
17
|
+
name: 'test',
|
|
18
|
+
});
|
|
19
|
+
await db.sync();
|
|
20
|
+
const repo = db.getRepository('test');
|
|
21
|
+
db.on('test.afterCreateWithAssociations', async (model, { transaction }) => {
|
|
22
|
+
throw new Error('test error');
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
await repo.create({
|
|
26
|
+
values: {},
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
}
|
|
30
|
+
const count = await repo.count();
|
|
31
|
+
expect(count).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Database, Migration, mockDatabase } from '@nocobase/database';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
const names = (migrations: Array<{ name: string }>) => migrations.map(m => m.name);
|
|
5
|
+
|
|
6
|
+
describe('migrator', () => {
|
|
7
|
+
let db: Database;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
|
|
11
|
+
db = mockDatabase({
|
|
12
|
+
tablePrefix: 'test_',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
await db.clean({ drop: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(async () => {
|
|
19
|
+
await db.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('addMigrations', async () => {
|
|
23
|
+
db.addMigrations({
|
|
24
|
+
directory: resolve(__dirname, './fixtures/migrations'),
|
|
25
|
+
});
|
|
26
|
+
await db.migrator.up();
|
|
27
|
+
expect(names(await db.migrator.executed())).toEqual(['m1', 'm2']);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('addMigrations', async () => {
|
|
31
|
+
db.addMigrations({
|
|
32
|
+
namespace: 'test',
|
|
33
|
+
directory: resolve(__dirname, './fixtures/migrations'),
|
|
34
|
+
});
|
|
35
|
+
await db.migrator.up();
|
|
36
|
+
expect(names(await db.migrator.executed())).toEqual(['test/m1', 'test/m2']);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('up and down', async () => {
|
|
40
|
+
const spy = jest.fn();
|
|
41
|
+
db.addMigration({
|
|
42
|
+
name: 'migration1',
|
|
43
|
+
migration: class extends Migration {
|
|
44
|
+
async up() {
|
|
45
|
+
spy('migration1-up');
|
|
46
|
+
}
|
|
47
|
+
async down() {
|
|
48
|
+
spy('migration1-down');
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
db.addMigration({
|
|
53
|
+
name: 'migration2',
|
|
54
|
+
migration: class extends Migration {
|
|
55
|
+
async up() {
|
|
56
|
+
spy('migration2-up');
|
|
57
|
+
}
|
|
58
|
+
async down() {
|
|
59
|
+
spy('migration2-down');
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
await db.migrator.up();
|
|
64
|
+
expect(names(await db.migrator.executed())).toEqual(['migration1', 'migration2']);
|
|
65
|
+
await db.migrator.down();
|
|
66
|
+
expect(names(await db.migrator.executed())).toEqual(['migration1']);
|
|
67
|
+
expect(spy).toHaveBeenCalledTimes(3);
|
|
68
|
+
expect(spy).toHaveBeenNthCalledWith(1, 'migration1-up');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Database } from '..';
|
|
2
|
+
import { mockDatabase } from '.';
|
|
3
|
+
|
|
4
|
+
describe('model hook', () => {
|
|
5
|
+
let db: Database;
|
|
6
|
+
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
db = mockDatabase();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await db.close();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('match', () => {
|
|
16
|
+
test('sequelize db hooks', async () => {
|
|
17
|
+
const matcher = db.modelHook.match('beforeDefine');
|
|
18
|
+
expect(matcher).toEqual('beforeDefine');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('sequelize global model hooks', async () => {
|
|
22
|
+
const matcher = db.modelHook.match('beforeCreate');
|
|
23
|
+
expect(matcher).toEqual('beforeCreate');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('sequelize model hooks without existing collection', async () => {
|
|
27
|
+
const matcher = db.modelHook.match('posts.beforeCreate');
|
|
28
|
+
expect(matcher).toEqual('beforeCreate');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('sequelize model hooks with existing collection', async () => {
|
|
32
|
+
db.collection({
|
|
33
|
+
name: 'posts',
|
|
34
|
+
fields: []
|
|
35
|
+
});
|
|
36
|
+
const matcher = db.modelHook.match('posts.beforeCreate');
|
|
37
|
+
expect(matcher).toEqual('beforeCreate');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('customized global hooks', async () => {
|
|
41
|
+
const matcher = db.modelHook.match('beforeDefineCollection');
|
|
42
|
+
expect(matcher).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('customized model hooks', async () => {
|
|
46
|
+
db.collection({
|
|
47
|
+
name: 'posts',
|
|
48
|
+
fields: []
|
|
49
|
+
});
|
|
50
|
+
const matcher = db.modelHook.match('posts.beforeCreateWithAssociations');
|
|
51
|
+
expect(matcher).toBeNull();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|