@nocobase/database 0.8.0-alpha.8 → 0.8.1-alpha.3
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.d.ts +7 -3
- package/lib/collection.js +126 -22
- package/lib/database.d.ts +13 -7
- package/lib/database.js +39 -8
- package/lib/decorators/must-have-filter-decorator.js +4 -0
- package/lib/decorators/transaction-decorator.js +1 -0
- package/lib/features/ReferencesMap.js +14 -9
- package/lib/field-repository/array-field-repository.d.ts +28 -0
- package/lib/field-repository/array-field-repository.js +208 -0
- package/lib/fields/array-field.d.ts +1 -1
- package/lib/fields/array-field.js +15 -11
- package/lib/fields/belongs-to-field.d.ts +9 -1
- package/lib/fields/belongs-to-field.js +21 -7
- package/lib/fields/belongs-to-many-field.d.ts +4 -0
- package/lib/fields/belongs-to-many-field.js +24 -0
- package/lib/fields/field.d.ts +3 -2
- package/lib/fields/field.js +19 -13
- package/lib/fields/has-many-field.d.ts +2 -1
- package/lib/fields/has-many-field.js +18 -10
- package/lib/fields/has-one-field.d.ts +1 -0
- package/lib/fields/has-one-field.js +22 -10
- package/lib/fields/index.d.ts +3 -3
- package/lib/fields/index.js +14 -34
- package/lib/fields/relation-field.d.ts +2 -1
- package/lib/fields/relation-field.js +16 -0
- package/lib/fields/set-field.d.ts +10 -0
- package/lib/fields/set-field.js +35 -0
- package/lib/fields/sort-field.d.ts +1 -1
- package/lib/fields/sort-field.js +1 -1
- package/lib/filter-match.d.ts +1 -0
- package/lib/filter-match.js +84 -0
- package/lib/filter-parser.d.ts +2 -2
- package/lib/index.d.ts +5 -1
- package/lib/index.js +56 -0
- package/lib/inherited-collection.d.ts +13 -0
- package/lib/inherited-collection.js +210 -0
- package/lib/inherited-map.d.ts +21 -0
- package/lib/inherited-map.js +203 -0
- package/lib/model-hook.d.ts +1 -1
- package/lib/model.d.ts +1 -0
- package/lib/model.js +47 -0
- package/lib/operators/array.d.ts +1 -25
- package/lib/operators/association.d.ts +1 -9
- package/lib/operators/boolean.d.ts +1 -12
- package/lib/operators/date.d.ts +1 -33
- package/lib/operators/empty.d.ts +2 -25
- package/lib/operators/ne.d.ts +1 -13
- package/lib/operators/notIn.d.ts +1 -9
- package/lib/options-parser.d.ts +3 -2
- package/lib/options-parser.js +16 -1
- package/lib/relation-repository/relation-repository.d.ts +2 -2
- package/lib/relation-repository/single-relation-repository.js +2 -2
- package/lib/repository.d.ts +18 -10
- package/lib/repository.js +172 -38
- package/lib/sync-runner.d.ts +4 -0
- package/lib/sync-runner.js +181 -0
- package/lib/types.d.ts +2 -2
- package/lib/update-associations.d.ts +1 -0
- package/lib/update-associations.js +21 -2
- package/lib/update-guard.d.ts +3 -3
- package/lib/utils.js +5 -0
- package/package.json +4 -4
- package/src/__tests__/bigint.test.ts +48 -0
- package/src/__tests__/collection.test.ts +48 -13
- package/src/__tests__/database.test.ts +10 -0
- package/src/__tests__/field-repository/array-field-repository.test.ts +94 -0
- package/src/__tests__/fields/set.test.ts +37 -0
- package/src/__tests__/filter-match.test.ts +52 -0
- package/src/__tests__/inhertits/collection-inherits-sync.test.ts +38 -0
- package/src/__tests__/inhertits/collection-inherits.test.ts +994 -0
- package/src/__tests__/inhertits/helper.ts +3 -0
- package/src/__tests__/inhertits/inherited-map.test.ts +27 -0
- package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +2 -0
- package/src/__tests__/relation-repository/has-many-repository.test.ts +1 -1
- package/src/__tests__/repository/destroy.test.ts +122 -1
- package/src/__tests__/repository/update-many.test.ts +57 -0
- package/src/__tests__/update-association-values.test.ts +232 -0
- package/src/__tests__/update-associations.test.ts +6 -1
- package/src/collection.ts +90 -8
- package/src/database.ts +53 -14
- package/src/decorators/must-have-filter-decorator.ts +4 -0
- package/src/decorators/transaction-decorator.ts +1 -0
- package/src/features/ReferencesMap.ts +20 -9
- package/src/features/referential-integrity-check.ts +1 -0
- package/src/field-repository/array-field-repository.ts +155 -0
- package/src/fields/array-field.ts +4 -4
- package/src/fields/belongs-to-field.ts +26 -10
- package/src/fields/belongs-to-many-field.ts +34 -0
- package/src/fields/field.ts +26 -13
- package/src/fields/has-many-field.ts +17 -9
- package/src/fields/has-one-field.ts +23 -9
- package/src/fields/index.ts +5 -5
- package/src/fields/relation-field.ts +16 -0
- package/src/fields/set-field.ts +25 -0
- package/src/fields/sort-field.ts +5 -4
- package/src/filter-match.ts +49 -0
- package/src/filter-parser.ts +2 -2
- package/src/index.ts +5 -2
- package/src/inherited-collection.ts +112 -0
- package/src/inherited-map.ts +97 -0
- package/src/model-hook.ts +2 -3
- package/src/model.ts +43 -3
- package/src/operators/array.ts +1 -1
- package/src/operators/association.ts +1 -1
- package/src/operators/boolean.ts +1 -1
- package/src/operators/date.ts +1 -1
- package/src/operators/empty.ts +1 -1
- package/src/operators/ne.ts +1 -1
- package/src/operators/notIn.ts +2 -1
- package/src/options-parser.ts +20 -4
- package/src/relation-repository/relation-repository.ts +2 -2
- package/src/relation-repository/single-relation-repository.ts +2 -2
- package/src/repository.ts +144 -30
- package/src/sync-runner.ts +162 -0
- package/src/types.ts +2 -2
- package/src/update-associations.ts +23 -7
- package/src/update-guard.ts +3 -3
- package/src/utils.ts +5 -1
- package/lib/fields/sequence-field.d.ts +0 -31
- package/lib/fields/sequence-field.js +0 -299
- package/src/__tests__/fields/sequence-field.test.ts +0 -455
- package/src/fields/sequence-field.ts +0 -200
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.SyncRunner = void 0;
|
|
7
|
+
|
|
8
|
+
function _lodash() {
|
|
9
|
+
const data = _interopRequireDefault(require("lodash"));
|
|
10
|
+
|
|
11
|
+
_lodash = function _lodash() {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
+
|
|
20
|
+
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; }
|
|
21
|
+
|
|
22
|
+
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; }
|
|
23
|
+
|
|
24
|
+
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; }
|
|
25
|
+
|
|
26
|
+
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; } } }; }
|
|
27
|
+
|
|
28
|
+
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); }
|
|
29
|
+
|
|
30
|
+
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; }
|
|
31
|
+
|
|
32
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
33
|
+
|
|
34
|
+
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); }); }; }
|
|
35
|
+
|
|
36
|
+
class SyncRunner {
|
|
37
|
+
static syncInheritModel(model, options) {
|
|
38
|
+
var _this = this;
|
|
39
|
+
|
|
40
|
+
return _asyncToGenerator(function* () {
|
|
41
|
+
const transaction = options.transaction;
|
|
42
|
+
const inheritedCollection = model.collection;
|
|
43
|
+
const db = inheritedCollection.context.database;
|
|
44
|
+
const dialect = db.sequelize.getDialect();
|
|
45
|
+
const queryInterface = db.sequelize.getQueryInterface();
|
|
46
|
+
|
|
47
|
+
if (dialect != 'postgres') {
|
|
48
|
+
throw new Error('Inherit model is only supported on postgres');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const parents = inheritedCollection.parents;
|
|
52
|
+
|
|
53
|
+
if (!parents) {
|
|
54
|
+
throw new Error(`Inherit model ${inheritedCollection.name} can't be created without parents, parents option is ${_lodash().default.castArray(inheritedCollection.options.inherits).join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const parentTables = parents.map(parent => parent.model.tableName);
|
|
58
|
+
const tableName = model.getTableName();
|
|
59
|
+
const attributes = model.tableAttributes;
|
|
60
|
+
|
|
61
|
+
const childAttributes = _lodash().default.pickBy(attributes, value => {
|
|
62
|
+
return !value.inherit;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
let maxSequenceVal = 0;
|
|
66
|
+
let maxSequenceName;
|
|
67
|
+
|
|
68
|
+
if (childAttributes.id && childAttributes.id.autoIncrement) {
|
|
69
|
+
var _iterator = _createForOfIteratorHelper(parentTables),
|
|
70
|
+
_step;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
74
|
+
const parent = _step.value;
|
|
75
|
+
const sequenceNameResult = yield queryInterface.sequelize.query(`SELECT column_default FROM information_schema.columns WHERE
|
|
76
|
+
table_name='${parent}' and "column_name" = 'id';`, {
|
|
77
|
+
transaction
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!sequenceNameResult[0].length) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const columnDefault = sequenceNameResult[0][0]['column_default'];
|
|
85
|
+
|
|
86
|
+
if (!columnDefault) {
|
|
87
|
+
throw new Error(`Can't find sequence name of ${parent}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const regex = new RegExp(/nextval\('("?\w+"?)\'.*\)/);
|
|
91
|
+
const match = regex.exec(columnDefault);
|
|
92
|
+
const sequenceName = match[1];
|
|
93
|
+
const sequenceCurrentValResult = yield queryInterface.sequelize.query(`select last_value from ${sequenceName}`, {
|
|
94
|
+
transaction
|
|
95
|
+
});
|
|
96
|
+
const sequenceCurrentVal = parseInt(sequenceCurrentValResult[0][0]['last_value']);
|
|
97
|
+
|
|
98
|
+
if (sequenceCurrentVal > maxSequenceVal) {
|
|
99
|
+
maxSequenceName = sequenceName;
|
|
100
|
+
maxSequenceVal = sequenceCurrentVal;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
_iterator.e(err);
|
|
105
|
+
} finally {
|
|
106
|
+
_iterator.f();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
yield _this.createTable(tableName, childAttributes, options, model, parentTables);
|
|
111
|
+
|
|
112
|
+
if (maxSequenceName) {
|
|
113
|
+
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(parent => db.getCollection(parent).model.tableName);
|
|
114
|
+
const sequenceTables = [...parentsDeep, tableName];
|
|
115
|
+
|
|
116
|
+
for (var _i = 0, _sequenceTables = sequenceTables; _i < _sequenceTables.length; _i++) {
|
|
117
|
+
const sequenceTable = _sequenceTables[_i];
|
|
118
|
+
const queryName = Boolean(sequenceTable.match(/[A-Z]/)) ? `"${sequenceTable}"` : sequenceTable;
|
|
119
|
+
const idColumnQuery = yield queryInterface.sequelize.query(`
|
|
120
|
+
SELECT true
|
|
121
|
+
FROM pg_attribute
|
|
122
|
+
WHERE attrelid = '${queryName}'::regclass -- cast to a registered class (table)
|
|
123
|
+
AND attname = 'id'
|
|
124
|
+
AND NOT attisdropped
|
|
125
|
+
`, {
|
|
126
|
+
transaction
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (idColumnQuery[0].length == 0) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
yield queryInterface.sequelize.query(`alter table "${sequenceTable}" alter column id set default nextval('${maxSequenceName}')`, {
|
|
134
|
+
transaction
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (options.alter) {
|
|
140
|
+
const columns = yield queryInterface.describeTable(tableName, options);
|
|
141
|
+
|
|
142
|
+
for (const columnName in childAttributes) {
|
|
143
|
+
if (!columns[columnName]) {
|
|
144
|
+
yield queryInterface.addColumn(tableName, columnName, childAttributes[columnName], options);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static createTable(tableName, attributes, options, model, parentTables) {
|
|
152
|
+
return _asyncToGenerator(function* () {
|
|
153
|
+
let sql = '';
|
|
154
|
+
options = _objectSpread({}, options);
|
|
155
|
+
|
|
156
|
+
if (options && options.uniqueKeys) {
|
|
157
|
+
_lodash().default.forOwn(options.uniqueKeys, uniqueKey => {
|
|
158
|
+
if (uniqueKey.customIndex === undefined) {
|
|
159
|
+
uniqueKey.customIndex = true;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (model) {
|
|
165
|
+
options.uniqueKeys = options.uniqueKeys || model.uniqueKeys;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const queryGenerator = model.queryGenerator;
|
|
169
|
+
attributes = _lodash().default.mapValues(attributes, attribute => model.sequelize.normalizeAttribute(attribute));
|
|
170
|
+
attributes = queryGenerator.attributesToSQL(attributes, {
|
|
171
|
+
table: tableName,
|
|
172
|
+
context: 'createTable'
|
|
173
|
+
});
|
|
174
|
+
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(';', ` INHERITS (${parentTables.map(t => `"${t}"`).join(', ')});`);
|
|
175
|
+
return yield model.sequelize.query(sql, options);
|
|
176
|
+
})();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
exports.SyncRunner = SyncRunner;
|
package/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Model } from './model';
|
|
2
|
-
import type { ValidationOptions } from 'sequelize/types/lib/instance-validator';
|
|
3
|
-
import type { HookReturn } from 'sequelize/types/lib/hooks';
|
|
4
2
|
import type { CreateOptions, DestroyOptions, SaveOptions, SyncOptions, UpdateOptions } from 'sequelize/types';
|
|
5
3
|
import { Collection, CollectionOptions } from './collection';
|
|
4
|
+
import { HookReturn } from 'sequelize/types/hooks';
|
|
5
|
+
import { ValidationOptions } from 'sequelize/types/instance-validator';
|
|
6
6
|
export declare type CollectionNameType = string;
|
|
7
7
|
export declare type ModelSyncEventType = 'beforeSync' | 'afterSync';
|
|
8
8
|
export declare type ModelValidateEventType = 'beforeValidate' | 'afterValidate';
|
|
@@ -22,6 +22,7 @@ interface UpdateAssociationOptions extends Transactionable, Hookable {
|
|
|
22
22
|
sourceModel?: Model;
|
|
23
23
|
context?: any;
|
|
24
24
|
associationContext?: any;
|
|
25
|
+
recursive?: boolean;
|
|
25
26
|
}
|
|
26
27
|
export declare function updateModelByValues(instance: Model, values: UpdateValue, options?: UpdateOptions): Promise<void>;
|
|
27
28
|
export declare function updateThroughTableValue(instance: Model, throughName: string, throughValues: any, source: Model, transaction?: any): Promise<any>;
|
|
@@ -151,6 +151,10 @@ function _updateAssociations() {
|
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
if (options === null || options === void 0 ? void 0 : options.updateAssociationValues) {
|
|
155
|
+
options.recursive = true;
|
|
156
|
+
}
|
|
157
|
+
|
|
154
158
|
let newTransaction = false;
|
|
155
159
|
let transaction = options.transaction;
|
|
156
160
|
|
|
@@ -306,7 +310,8 @@ function _updateSingleAssociation() {
|
|
|
306
310
|
throw new Error(`The value of '${key}' cannot be in array format`);
|
|
307
311
|
}
|
|
308
312
|
|
|
309
|
-
const
|
|
313
|
+
const recursive = options.recursive,
|
|
314
|
+
context = options.context,
|
|
310
315
|
_options$updateAssoci = options.updateAssociationValues,
|
|
311
316
|
updateAssociationValues = _options$updateAssoci === void 0 ? [] : _options$updateAssoci,
|
|
312
317
|
transaction = options.transaction;
|
|
@@ -378,6 +383,10 @@ function _updateSingleAssociation() {
|
|
|
378
383
|
transaction
|
|
379
384
|
});
|
|
380
385
|
|
|
386
|
+
if (!recursive) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
381
390
|
if (updateAssociationValues.includes(key)) {
|
|
382
391
|
yield instance.update(value, _objectSpread(_objectSpread({}, options), {}, {
|
|
383
392
|
transaction
|
|
@@ -431,7 +440,8 @@ function _updateMultipleAssociation() {
|
|
|
431
440
|
return false;
|
|
432
441
|
}
|
|
433
442
|
|
|
434
|
-
const
|
|
443
|
+
const recursive = options.recursive,
|
|
444
|
+
context = options.context,
|
|
435
445
|
_options$updateAssoci2 = options.updateAssociationValues,
|
|
436
446
|
updateAssociationValues = _options$updateAssoci2 === void 0 ? [] : _options$updateAssoci2,
|
|
437
447
|
transaction = options.transaction;
|
|
@@ -528,9 +538,18 @@ function _updateMultipleAssociation() {
|
|
|
528
538
|
const instance = yield association.target.findByPk(item[pk], {
|
|
529
539
|
transaction
|
|
530
540
|
});
|
|
541
|
+
|
|
542
|
+
if (!instance) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
|
|
531
546
|
const addAccessor = association.accessors.add;
|
|
532
547
|
yield model[addAccessor](item[pk], accessorOptions);
|
|
533
548
|
|
|
549
|
+
if (!recursive) {
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
|
|
534
553
|
if (updateAssociationValues.includes(key)) {
|
|
535
554
|
yield instance.update(item, _objectSpread(_objectSpread({}, options), {}, {
|
|
536
555
|
transaction
|
package/lib/update-guard.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ModelStatic } from 'sequelize';
|
|
2
2
|
import { AssociationKeysToBeUpdate, BlackList, WhiteList } from './repository';
|
|
3
3
|
declare type UpdateValueItem = string | number | UpdateValues;
|
|
4
4
|
declare type UpdateValues = {
|
|
@@ -6,13 +6,13 @@ declare type UpdateValues = {
|
|
|
6
6
|
};
|
|
7
7
|
declare type UpdateAction = 'create' | 'update';
|
|
8
8
|
export declare class UpdateGuard {
|
|
9
|
-
model:
|
|
9
|
+
model: ModelStatic<any>;
|
|
10
10
|
action: UpdateAction;
|
|
11
11
|
private associationKeysToBeUpdate;
|
|
12
12
|
private blackList;
|
|
13
13
|
private whiteList;
|
|
14
14
|
setAction(action: UpdateAction): void;
|
|
15
|
-
setModel(model:
|
|
15
|
+
setModel(model: ModelStatic<any>): void;
|
|
16
16
|
setAssociationKeysToBeUpdate(associationKeysToBeUpdate: AssociationKeysToBeUpdate): void;
|
|
17
17
|
setWhiteList(whiteList: WhiteList): void;
|
|
18
18
|
setBlackList(blackList: BlackList): void;
|
package/lib/utils.js
CHANGED
|
@@ -45,6 +45,11 @@ function _handleAppendsQuery() {
|
|
|
45
45
|
_handleAppendsQuery = _asyncToGenerator(function* (options) {
|
|
46
46
|
const templateModel = options.templateModel,
|
|
47
47
|
queryPromises = options.queryPromises;
|
|
48
|
+
|
|
49
|
+
if (!templateModel) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
const primaryKey = templateModel.constructor.primaryKeyAttribute;
|
|
49
54
|
const results = yield Promise.all(queryPromises);
|
|
50
55
|
let rows;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/database",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1-alpha.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@nocobase/utils": "0.8.
|
|
15
|
+
"@nocobase/utils": "0.8.1-alpha.3",
|
|
16
16
|
"async-mutex": "^0.3.2",
|
|
17
17
|
"cron-parser": "4.4.0",
|
|
18
18
|
"deepmerge": "^4.2.2",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"mathjs": "^10.6.1",
|
|
22
22
|
"moment": "2.x",
|
|
23
23
|
"semver": "^7.3.7",
|
|
24
|
-
"sequelize": "^6.
|
|
24
|
+
"sequelize": "^6.26.0",
|
|
25
25
|
"umzug": "^3.1.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
@@ -32,5 +32,5 @@
|
|
|
32
32
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
33
33
|
"directory": "packages/database"
|
|
34
34
|
},
|
|
35
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "e03df3df5962b99d9fbf5b6e33fbe2b23f14f3d3"
|
|
36
36
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Database } from '../database';
|
|
2
|
+
import { mockDatabase } from './index';
|
|
3
|
+
|
|
4
|
+
const excludeSqlite = () => (process.env.DB_DIALECT != 'sqlite' ? describe : describe.skip);
|
|
5
|
+
|
|
6
|
+
excludeSqlite()('collection', () => {
|
|
7
|
+
let db: Database;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
db = mockDatabase({
|
|
11
|
+
logging: console.log,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await db.clean({ drop: true });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
await db.close();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should using bigint for id field', async () => {
|
|
22
|
+
const collection = db.collection({
|
|
23
|
+
name: 'users',
|
|
24
|
+
fields: [{ type: 'hasOne', name: 'profile' }],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await db.sync();
|
|
28
|
+
const tableInfo = await db.sequelize.getQueryInterface().describeTable(collection.model.tableName);
|
|
29
|
+
|
|
30
|
+
expect(tableInfo['id'].type).toBe('BIGINT');
|
|
31
|
+
|
|
32
|
+
const profile = db.collection({
|
|
33
|
+
name: 'profiles',
|
|
34
|
+
fields: [
|
|
35
|
+
{
|
|
36
|
+
type: 'belongsTo',
|
|
37
|
+
name: 'user',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await db.sync();
|
|
43
|
+
|
|
44
|
+
const profileTableInfo = await db.sequelize.getQueryInterface().describeTable(profile.model.tableName);
|
|
45
|
+
|
|
46
|
+
expect(profileTableInfo['userId'].type).toBe('BIGINT');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -3,31 +3,66 @@ import { Database } from '../database';
|
|
|
3
3
|
import { mockDatabase } from './index';
|
|
4
4
|
import { IdentifierError } from '../errors/identifier-error';
|
|
5
5
|
|
|
6
|
+
const pgOnly = () => (process.env.DB_DIALECT == 'postgres' ? it : it.skip);
|
|
6
7
|
describe('collection', () => {
|
|
7
8
|
let db: Database;
|
|
8
9
|
|
|
9
10
|
beforeEach(async () => {
|
|
10
|
-
db = mockDatabase(
|
|
11
|
+
db = mockDatabase({
|
|
12
|
+
logging: console.log,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
await db.clean({ drop: true });
|
|
11
16
|
});
|
|
12
17
|
|
|
13
18
|
afterEach(async () => {
|
|
14
19
|
await db.close();
|
|
15
20
|
});
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
it('should throw error when create empty collection in sqlite and mysql', async () => {
|
|
23
|
+
if (!db.inDialect('sqlite', 'mysql')) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
db.collection({
|
|
28
|
+
name: 'empty',
|
|
29
|
+
timestamps: false,
|
|
30
|
+
autoGenId: false,
|
|
31
|
+
fields: [],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
let error;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await db.sync({
|
|
38
|
+
force: false,
|
|
39
|
+
alter: {
|
|
40
|
+
drop: false,
|
|
26
41
|
},
|
|
27
|
-
|
|
42
|
+
});
|
|
43
|
+
} catch (e) {
|
|
44
|
+
error = e;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
expect(error.message.includes("Zero-column tables aren't supported in")).toBeTruthy();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
pgOnly()('can create empty collection', async () => {
|
|
51
|
+
db.collection({
|
|
52
|
+
name: 'empty',
|
|
53
|
+
timestamps: false,
|
|
54
|
+
autoGenId: false,
|
|
55
|
+
fields: [],
|
|
28
56
|
});
|
|
29
|
-
|
|
30
|
-
await db.sync(
|
|
57
|
+
|
|
58
|
+
await db.sync({
|
|
59
|
+
force: false,
|
|
60
|
+
alter: {
|
|
61
|
+
drop: false,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(db.getCollection('empty')).toBeInstanceOf(Collection);
|
|
31
66
|
});
|
|
32
67
|
|
|
33
68
|
test('removeFromDb', async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { Database, Model } from '..';
|
|
3
|
+
import { ArrayFieldRepository } from '../field-repository/array-field-repository';
|
|
3
4
|
import { mockDatabase } from './index';
|
|
4
5
|
|
|
5
6
|
describe('database', () => {
|
|
@@ -47,6 +48,15 @@ describe('database', () => {
|
|
|
47
48
|
);
|
|
48
49
|
});
|
|
49
50
|
|
|
51
|
+
it('should get array field repository', async () => {
|
|
52
|
+
db.collection({
|
|
53
|
+
name: 'tests',
|
|
54
|
+
fields: [{ type: 'set', name: 'set-field' }],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(db.getRepository('tests.set-field', '1')).toBeInstanceOf(ArrayFieldRepository);
|
|
58
|
+
});
|
|
59
|
+
|
|
50
60
|
test('import', async () => {
|
|
51
61
|
await db.import({
|
|
52
62
|
directory: path.resolve(__dirname, './fixtures/collections'),
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { mockDatabase } from '../index';
|
|
2
|
+
import Database from '../../database';
|
|
3
|
+
import { ArrayFieldRepository } from '../../field-repository/array-field-repository';
|
|
4
|
+
|
|
5
|
+
describe('Array field repository', () => {
|
|
6
|
+
let db: Database;
|
|
7
|
+
|
|
8
|
+
let TestCollection;
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
db = mockDatabase();
|
|
12
|
+
TestCollection = db.collection({
|
|
13
|
+
name: 'test',
|
|
14
|
+
fields: [
|
|
15
|
+
{
|
|
16
|
+
type: 'set',
|
|
17
|
+
name: 'set-field',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await db.sync();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
await db.close();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should add item into fields', async () => {
|
|
30
|
+
const a1 = await TestCollection.repository.create({});
|
|
31
|
+
|
|
32
|
+
const fieldRepository = new ArrayFieldRepository(TestCollection, 'set-field', a1.get('id'));
|
|
33
|
+
await fieldRepository.add({
|
|
34
|
+
values: 'a',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(await fieldRepository.get()).toEqual(['a']);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should remove item', async () => {
|
|
41
|
+
const a1 = await TestCollection.repository.create({});
|
|
42
|
+
|
|
43
|
+
const fieldRepository = new ArrayFieldRepository(TestCollection, 'set-field', a1.get('id'));
|
|
44
|
+
await fieldRepository.add({
|
|
45
|
+
values: ['a', 'b', 'c'],
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(await fieldRepository.get()).toEqual(['a', 'b', 'c']);
|
|
49
|
+
await fieldRepository.remove({
|
|
50
|
+
values: ['c'],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(await fieldRepository.get()).toEqual(['a', 'b']);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should set items', async () => {
|
|
57
|
+
const a1 = await TestCollection.repository.create({});
|
|
58
|
+
|
|
59
|
+
const fieldRepository = new ArrayFieldRepository(TestCollection, 'set-field', a1.get('id'));
|
|
60
|
+
await fieldRepository.add({
|
|
61
|
+
values: ['a', 'b', 'c'],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(await fieldRepository.get()).toEqual(['a', 'b', 'c']);
|
|
65
|
+
await fieldRepository.set({
|
|
66
|
+
values: ['d', 'e'],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(await fieldRepository.get()).toEqual(['d', 'e']);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should toggle item', async () => {
|
|
73
|
+
const a1 = await TestCollection.repository.create({});
|
|
74
|
+
|
|
75
|
+
const fieldRepository = new ArrayFieldRepository(TestCollection, 'set-field', a1.get('id'));
|
|
76
|
+
await fieldRepository.add({
|
|
77
|
+
values: ['a', 'b', 'c'],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(await fieldRepository.get()).toEqual(['a', 'b', 'c']);
|
|
81
|
+
|
|
82
|
+
await fieldRepository.toggle({
|
|
83
|
+
value: 'c',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(await fieldRepository.get()).toEqual(['a', 'b']);
|
|
87
|
+
|
|
88
|
+
await fieldRepository.toggle({
|
|
89
|
+
value: 'c',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(await fieldRepository.get()).toEqual(['a', 'b', 'c']);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mockDatabase } from '../';
|
|
2
|
+
import { Database } from '../../database';
|
|
3
|
+
|
|
4
|
+
describe('set field', () => {
|
|
5
|
+
let db: Database;
|
|
6
|
+
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
db = mockDatabase();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await db.close();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should set Set field', async () => {
|
|
16
|
+
const A = db.collection({
|
|
17
|
+
name: 'a',
|
|
18
|
+
fields: [
|
|
19
|
+
{
|
|
20
|
+
type: 'set',
|
|
21
|
+
name: 'set',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await db.sync();
|
|
27
|
+
|
|
28
|
+
const a = await A.repository.create({});
|
|
29
|
+
|
|
30
|
+
a.set('set', ['a', 'b', 'c', 'a']);
|
|
31
|
+
|
|
32
|
+
await a.save();
|
|
33
|
+
|
|
34
|
+
const setValue = a.get('set');
|
|
35
|
+
expect(setValue).toEqual(['a', 'b', 'c']);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Database, Model } from '..';
|
|
2
|
+
import { filterMatch } from '../filter-match';
|
|
3
|
+
import { mockDatabase } from './index';
|
|
4
|
+
|
|
5
|
+
describe('filterMatch', () => {
|
|
6
|
+
let db: Database;
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
db = mockDatabase();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await db.close();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('filter match', async () => {
|
|
17
|
+
const Post = db.collection({
|
|
18
|
+
name: 'posts',
|
|
19
|
+
fields: [{ type: 'string', name: 'title' }],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await db.sync();
|
|
23
|
+
|
|
24
|
+
const post = await Post.repository.create({
|
|
25
|
+
values: { title: 't1' },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(
|
|
29
|
+
filterMatch(post, {
|
|
30
|
+
title: 't1',
|
|
31
|
+
}),
|
|
32
|
+
).toBeTruthy();
|
|
33
|
+
|
|
34
|
+
expect(
|
|
35
|
+
filterMatch(post, {
|
|
36
|
+
$or: [{ title: 't1' }, { title: 't2' }],
|
|
37
|
+
}),
|
|
38
|
+
).toBeTruthy();
|
|
39
|
+
|
|
40
|
+
expect(
|
|
41
|
+
filterMatch(post, {
|
|
42
|
+
$and: [{ title: 't1' }, { title: 't2' }],
|
|
43
|
+
}),
|
|
44
|
+
).toBeFalsy();
|
|
45
|
+
|
|
46
|
+
expect(
|
|
47
|
+
filterMatch(post, {
|
|
48
|
+
title: 't2',
|
|
49
|
+
}),
|
|
50
|
+
).toBeFalsy();
|
|
51
|
+
});
|
|
52
|
+
});
|