@nocobase/database 0.11.1-alpha.3 → 0.11.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/options-parser.js +13 -0
- package/lib/sync-runner.js +1 -1
- package/package.json +4 -4
- package/src/__tests__/option-parser.test.ts +92 -58
- package/src/__tests__/repository/find.test.ts +24 -14
- package/src/__tests__/view/view-collection.test.ts +131 -107
- package/src/options-parser.ts +22 -0
- package/src/sync-runner.ts +4 -2
package/lib/options-parser.js
CHANGED
|
@@ -249,6 +249,8 @@ class OptionsParser {
|
|
|
249
249
|
}
|
|
250
250
|
parseAppends(appends, filterParams) {
|
|
251
251
|
if (!appends) return filterParams;
|
|
252
|
+
// sort appends by path length
|
|
253
|
+
appends = _lodash().default.sortBy(appends, append => append.split('.').length);
|
|
252
254
|
/**
|
|
253
255
|
* set include params
|
|
254
256
|
* @param model
|
|
@@ -256,6 +258,7 @@ class OptionsParser {
|
|
|
256
258
|
* @param append
|
|
257
259
|
*/
|
|
258
260
|
const setInclude = (model, queryParams, append) => {
|
|
261
|
+
var _lodash$get;
|
|
259
262
|
const appendFields = append.split('.');
|
|
260
263
|
const appendAssociation = appendFields[0];
|
|
261
264
|
const associations = model.associations;
|
|
@@ -286,6 +289,16 @@ class OptionsParser {
|
|
|
286
289
|
// if include from filter, remove fromFilter attribute
|
|
287
290
|
if (existIncludeIndex != -1) {
|
|
288
291
|
delete queryParams['include'][existIncludeIndex]['fromFilter'];
|
|
292
|
+
// set include attributes to all attributes
|
|
293
|
+
if (Array.isArray(queryParams['include'][existIncludeIndex]['attributes']) && queryParams['include'][existIncludeIndex]['attributes'].length == 0) {
|
|
294
|
+
queryParams['include'][existIncludeIndex]['attributes'] = {
|
|
295
|
+
include: []
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (lastLevel && existIncludeIndex != -1 && ((_lodash$get = _lodash().default.get(queryParams, ['include', existIncludeIndex, 'attributes', 'include'])) === null || _lodash$get === void 0 ? void 0 : _lodash$get.length) == 0) {
|
|
300
|
+
// if append is last level and association exists, ignore it
|
|
301
|
+
return;
|
|
289
302
|
}
|
|
290
303
|
// if association not exist, create it
|
|
291
304
|
if (existIncludeIndex == -1) {
|
package/lib/sync-runner.js
CHANGED
|
@@ -64,7 +64,7 @@ class SyncRunner {
|
|
|
64
64
|
}
|
|
65
65
|
const columnDefault = sequenceNameResult[0][0]['column_default'];
|
|
66
66
|
if (!columnDefault) {
|
|
67
|
-
throw new Error(`Can't find sequence name of ${parent}`);
|
|
67
|
+
throw new Error(`Can't find sequence name of parent collection ${parent.options.name}`);
|
|
68
68
|
}
|
|
69
69
|
const regex = new RegExp(/nextval\('(.*)'::regclass\)/);
|
|
70
70
|
const match = regex.exec(columnDefault);
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/database",
|
|
3
|
-
"version": "0.11.1-alpha.
|
|
3
|
+
"version": "0.11.1-alpha.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@nocobase/logger": "0.11.1-alpha.
|
|
10
|
-
"@nocobase/utils": "0.11.1-alpha.
|
|
9
|
+
"@nocobase/logger": "0.11.1-alpha.4",
|
|
10
|
+
"@nocobase/utils": "0.11.1-alpha.4",
|
|
11
11
|
"async-mutex": "^0.3.2",
|
|
12
12
|
"cron-parser": "4.4.0",
|
|
13
13
|
"dayjs": "^1.11.8",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
30
30
|
"directory": "packages/database"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "d9b5bde913013f1057e1aab49587eb0ad3dcb06e"
|
|
33
33
|
}
|
|
@@ -112,80 +112,114 @@ describe('option parser', () => {
|
|
|
112
112
|
]);
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
describe('options parser with fields option', () => {
|
|
116
|
+
it('should handle field and association', () => {
|
|
117
|
+
const options: any = {
|
|
118
|
+
fields: ['id', 'posts'],
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// 转换为 attributes: ['id'], include: [{association: 'posts'}]
|
|
122
|
+
const parser = new OptionsParser(options, {
|
|
123
|
+
collection: User,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const params = parser.toSequelizeParams();
|
|
127
|
+
|
|
128
|
+
console.log(params);
|
|
129
|
+
expect(params['attributes']).toContain('id');
|
|
130
|
+
expect(params['include'][0]['association']).toEqual('posts');
|
|
122
131
|
});
|
|
123
|
-
let params = parser.toSequelizeParams();
|
|
124
132
|
|
|
125
|
-
|
|
126
|
-
|
|
133
|
+
it('should handle field with association', () => {
|
|
134
|
+
const options = {
|
|
135
|
+
appends: ['posts'],
|
|
136
|
+
};
|
|
127
137
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
const parser = new OptionsParser(options, {
|
|
139
|
+
collection: User,
|
|
140
|
+
});
|
|
141
|
+
const params = parser.toSequelizeParams();
|
|
132
142
|
|
|
133
|
-
|
|
134
|
-
|
|
143
|
+
expect(params['attributes']['include']).toEqual([]);
|
|
144
|
+
expect(params['include'][0]['association']).toEqual('posts');
|
|
135
145
|
});
|
|
136
|
-
params = parser.toSequelizeParams();
|
|
137
|
-
expect(params['attributes']['include']).toEqual([]);
|
|
138
|
-
expect(params['include'][0]['association']).toEqual('posts');
|
|
139
146
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
147
|
+
it('should handle field with association field', () => {
|
|
148
|
+
// fields with association field
|
|
149
|
+
const options = {
|
|
150
|
+
fields: ['id', 'posts.title'],
|
|
151
|
+
};
|
|
144
152
|
|
|
145
|
-
|
|
146
|
-
|
|
153
|
+
const parser = new OptionsParser(options, {
|
|
154
|
+
collection: User,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const params = parser.toSequelizeParams();
|
|
158
|
+
expect(params['attributes']).toContain('id');
|
|
159
|
+
expect(params['include'][0]['association']).toEqual('posts');
|
|
160
|
+
expect(params['include'][0]['attributes']).toContain('title');
|
|
147
161
|
});
|
|
148
|
-
params = parser.toSequelizeParams();
|
|
149
|
-
expect(params['attributes']).toContain('id');
|
|
150
|
-
expect(params['include'][0]['association']).toEqual('posts');
|
|
151
|
-
expect(params['include'][0]['attributes']).toContain('title');
|
|
152
162
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
163
|
+
it('should handle nested fields option', () => {
|
|
164
|
+
const options = {
|
|
165
|
+
fields: ['posts', 'posts.title'],
|
|
166
|
+
};
|
|
157
167
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
params = parser.toSequelizeParams();
|
|
162
|
-
expect(params['attributes']).toContain('id');
|
|
163
|
-
expect(params['include'][0]['association']).toEqual('posts');
|
|
164
|
-
expect(params['include'][0]['attributes']).toEqual({ include: [] });
|
|
165
|
-
expect(params['include'][0]['include'][0]['association']).toEqual('comments');
|
|
168
|
+
const parser = new OptionsParser(options, {
|
|
169
|
+
collection: User,
|
|
170
|
+
});
|
|
166
171
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
};
|
|
171
|
-
parser = new OptionsParser(options, {
|
|
172
|
-
collection: User,
|
|
172
|
+
const params = parser.toSequelizeParams();
|
|
173
|
+
const postAssociationParams = params['include'][0];
|
|
174
|
+
expect(postAssociationParams['attributes']).toEqual({ include: [] });
|
|
173
175
|
});
|
|
174
|
-
params = parser.toSequelizeParams();
|
|
175
|
-
expect(params['attributes']['exclude']).toContain('id');
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
it('should handle fields with association & association field', () => {
|
|
178
|
+
// fields with nested field
|
|
179
|
+
const options = {
|
|
180
|
+
fields: ['id', 'posts', 'posts.comments.content'],
|
|
181
|
+
};
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
const parser = new OptionsParser(options, {
|
|
184
|
+
collection: User,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const params = parser.toSequelizeParams();
|
|
188
|
+
const postAssociationParams = params['include'][0];
|
|
189
|
+
|
|
190
|
+
expect(params['attributes']).toContain('id');
|
|
191
|
+
expect(postAssociationParams['association']).toEqual('posts');
|
|
192
|
+
expect(postAssociationParams['attributes']).toEqual({ include: [] });
|
|
193
|
+
expect(postAssociationParams['include'][0]['association']).toEqual('comments');
|
|
185
194
|
});
|
|
186
|
-
params = parser.toSequelizeParams();
|
|
187
195
|
|
|
188
|
-
|
|
196
|
+
it('should handle except option', () => {
|
|
197
|
+
// fields with expect
|
|
198
|
+
const options = {
|
|
199
|
+
except: ['id'],
|
|
200
|
+
};
|
|
201
|
+
const parser = new OptionsParser(options, {
|
|
202
|
+
collection: User,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const params = parser.toSequelizeParams();
|
|
206
|
+
expect(params['attributes']['exclude']).toContain('id');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should handle fields with except option', () => {
|
|
210
|
+
// expect with association
|
|
211
|
+
const options = {
|
|
212
|
+
fields: ['posts'],
|
|
213
|
+
except: ['posts.id'],
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const parser = new OptionsParser(options, {
|
|
217
|
+
collection: User,
|
|
218
|
+
});
|
|
219
|
+
const params = parser.toSequelizeParams();
|
|
220
|
+
|
|
221
|
+
expect(params['include'][0]['attributes']['exclude']).toContain('id');
|
|
222
|
+
});
|
|
189
223
|
});
|
|
190
224
|
|
|
191
225
|
test('option parser with multiple association', () => {
|
|
@@ -378,23 +378,33 @@ describe('repository find', () => {
|
|
|
378
378
|
});
|
|
379
379
|
});
|
|
380
380
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
association: 'profile',
|
|
387
|
-
attributes: ['salary'],
|
|
388
|
-
},
|
|
389
|
-
],
|
|
390
|
-
});
|
|
381
|
+
describe('find with fields', () => {
|
|
382
|
+
it('should only output filed in fields args', async () => {
|
|
383
|
+
const users = await User.repository.find({
|
|
384
|
+
fields: ['profile.salary'],
|
|
385
|
+
});
|
|
391
386
|
|
|
392
|
-
|
|
393
|
-
|
|
387
|
+
const firstUser = users[0].toJSON();
|
|
388
|
+
expect(Object.keys(firstUser)).toEqual(['profile']);
|
|
389
|
+
expect(Object.keys(firstUser.profile)).toEqual(['salary']);
|
|
394
390
|
});
|
|
395
391
|
|
|
396
|
-
|
|
397
|
-
|
|
392
|
+
it('should output all fields when field has relation field', async () => {
|
|
393
|
+
const users = await User.repository.find({
|
|
394
|
+
fields: ['profile.salary', 'profile'],
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const firstUser = users[0].toJSON();
|
|
398
|
+
expect(Object.keys(firstUser)).toEqual(['profile']);
|
|
399
|
+
expect(Object.keys(firstUser.profile)).toEqual([
|
|
400
|
+
'id',
|
|
401
|
+
'createdAt',
|
|
402
|
+
'updatedAt',
|
|
403
|
+
'salary',
|
|
404
|
+
'userId',
|
|
405
|
+
'description',
|
|
406
|
+
]);
|
|
407
|
+
});
|
|
398
408
|
});
|
|
399
409
|
|
|
400
410
|
it('append with associations', async () => {
|
|
@@ -18,131 +18,155 @@ pgOnly()('', () => {
|
|
|
18
18
|
await db.close();
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
type: 'integer',
|
|
44
|
-
name: 'count',
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
type: 'belongsTo',
|
|
48
|
-
name: 'item',
|
|
49
|
-
target: 'items',
|
|
50
|
-
foreignKey: 'item_id',
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
type: 'belongsTo',
|
|
54
|
-
name: 'order',
|
|
55
|
-
target: 'orders',
|
|
56
|
-
foreignKey: 'order_id',
|
|
57
|
-
onDelete: 'NO ACTION',
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const Item = db.collection({
|
|
63
|
-
name: 'items',
|
|
64
|
-
fields: [{ name: 'name', type: 'string' }],
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
await db.sync();
|
|
68
|
-
|
|
69
|
-
const viewName = 'order_item_view';
|
|
21
|
+
describe('view as through table', () => {
|
|
22
|
+
let Order;
|
|
23
|
+
let OrderItem;
|
|
24
|
+
let Item;
|
|
25
|
+
let OrderItemView;
|
|
26
|
+
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
Order = db.collection({
|
|
29
|
+
name: 'orders',
|
|
30
|
+
fields: [
|
|
31
|
+
{
|
|
32
|
+
type: 'string',
|
|
33
|
+
name: 'name',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'hasMany',
|
|
37
|
+
name: 'orderItems',
|
|
38
|
+
foreignKey: 'order_id',
|
|
39
|
+
target: 'orderItems',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
});
|
|
70
43
|
|
|
71
|
-
|
|
72
|
-
|
|
44
|
+
OrderItem = db.collection({
|
|
45
|
+
name: 'orderItems',
|
|
46
|
+
timestamps: false,
|
|
47
|
+
fields: [
|
|
48
|
+
{
|
|
49
|
+
type: 'integer',
|
|
50
|
+
name: 'count',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'belongsTo',
|
|
54
|
+
name: 'item',
|
|
55
|
+
target: 'items',
|
|
56
|
+
foreignKey: 'item_id',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'belongsTo',
|
|
60
|
+
name: 'order',
|
|
61
|
+
target: 'orders',
|
|
62
|
+
foreignKey: 'order_id',
|
|
63
|
+
onDelete: 'NO ACTION',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
73
67
|
|
|
74
|
-
|
|
68
|
+
Item = db.collection({
|
|
69
|
+
name: 'items',
|
|
70
|
+
fields: [{ name: 'name', type: 'string' }],
|
|
71
|
+
});
|
|
75
72
|
|
|
76
|
-
|
|
73
|
+
await db.sync();
|
|
77
74
|
|
|
78
|
-
|
|
79
|
-
name: viewName,
|
|
80
|
-
view: true,
|
|
81
|
-
schema: db.inDialect('postgres') ? 'public' : undefined,
|
|
82
|
-
fields: [
|
|
83
|
-
{
|
|
84
|
-
type: 'bigInt',
|
|
85
|
-
name: 'order_id',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
type: 'bigInt',
|
|
89
|
-
name: 'item_id',
|
|
90
|
-
onDelete: 'CASCADE',
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
});
|
|
75
|
+
const viewName = 'order_item_view';
|
|
94
76
|
|
|
95
|
-
|
|
77
|
+
const dropViewSQL = `DROP VIEW IF EXISTS ${viewName}`;
|
|
78
|
+
await db.sequelize.query(dropViewSQL);
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
type: 'belongsToMany',
|
|
99
|
-
target: 'orderItems',
|
|
100
|
-
through: viewName,
|
|
101
|
-
foreignKey: 'order_id',
|
|
102
|
-
otherKey: 'item_id',
|
|
103
|
-
sourceKey: 'id',
|
|
104
|
-
targetKey: 'id',
|
|
105
|
-
onDelete: 'CASCADE',
|
|
106
|
-
});
|
|
80
|
+
const viewSQL = `CREATE VIEW ${viewName} as SELECT order_item.order_id as order_id, order_item.item_id as item_id, items.name as item_name FROM ${OrderItem.quotedTableName()} as order_item INNER JOIN ${Item.quotedTableName()} as items ON order_item.item_id = items.id`;
|
|
107
81
|
|
|
108
|
-
|
|
82
|
+
await db.sequelize.query(viewSQL);
|
|
109
83
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
84
|
+
OrderItemView = db.collection({
|
|
85
|
+
name: viewName,
|
|
86
|
+
view: true,
|
|
87
|
+
schema: db.inDialect('postgres') ? 'public' : undefined,
|
|
88
|
+
fields: [
|
|
114
89
|
{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
name: 'item1',
|
|
118
|
-
},
|
|
90
|
+
type: 'bigInt',
|
|
91
|
+
name: 'order_id',
|
|
119
92
|
},
|
|
120
93
|
{
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
},
|
|
94
|
+
type: 'bigInt',
|
|
95
|
+
name: 'item_id',
|
|
96
|
+
onDelete: 'CASCADE',
|
|
125
97
|
},
|
|
126
98
|
],
|
|
127
|
-
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await db.sync();
|
|
102
|
+
|
|
103
|
+
Order.setField('items', {
|
|
104
|
+
type: 'belongsToMany',
|
|
105
|
+
target: 'items',
|
|
106
|
+
through: viewName,
|
|
107
|
+
foreignKey: 'order_id',
|
|
108
|
+
otherKey: 'item_id',
|
|
109
|
+
sourceKey: 'id',
|
|
110
|
+
targetKey: 'id',
|
|
111
|
+
onDelete: 'CASCADE',
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await db.sync();
|
|
115
|
+
|
|
116
|
+
await db.getRepository('orders').create({
|
|
117
|
+
values: {
|
|
118
|
+
name: 'order1',
|
|
119
|
+
orderItems: [
|
|
120
|
+
{
|
|
121
|
+
count: 1,
|
|
122
|
+
item: {
|
|
123
|
+
name: 'item1',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
count: 2,
|
|
128
|
+
item: {
|
|
129
|
+
name: 'item2',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
});
|
|
128
135
|
});
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
137
|
+
it('should skip on delete on view collection', async () => {
|
|
138
|
+
const order1 = await db.getRepository('orders').findOne({});
|
|
139
|
+
|
|
140
|
+
const item1 = await db.getRepository('items').findOne({
|
|
141
|
+
filter: {
|
|
142
|
+
name: 'item1',
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
let error;
|
|
147
|
+
try {
|
|
148
|
+
await db.getRepository('orders').destroy({
|
|
149
|
+
filterByTk: order1.get('id'),
|
|
150
|
+
});
|
|
151
|
+
} catch (err) {
|
|
152
|
+
error = err;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
expect(error).toBeUndefined();
|
|
134
156
|
});
|
|
135
157
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
158
|
+
it('should filter by view collection as through table', async () => {
|
|
159
|
+
const orders = await db.getRepository('orders').find({
|
|
160
|
+
appends: ['items'],
|
|
161
|
+
filter: {
|
|
162
|
+
items: {
|
|
163
|
+
name: 'not exists',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
140
166
|
});
|
|
141
|
-
} catch (err) {
|
|
142
|
-
error = err;
|
|
143
|
-
}
|
|
144
167
|
|
|
145
|
-
|
|
168
|
+
expect(orders).toHaveLength(0);
|
|
169
|
+
});
|
|
146
170
|
});
|
|
147
171
|
|
|
148
172
|
it('should update view collection', async () => {
|
|
@@ -196,7 +220,7 @@ pgOnly()('', () => {
|
|
|
196
220
|
});
|
|
197
221
|
|
|
198
222
|
// create INSTEAD OF INSERT trigger
|
|
199
|
-
await db.sequelize.query(`
|
|
223
|
+
await db.sequelize.query(`
|
|
200
224
|
CREATE OR REPLACE FUNCTION insert_users_with_group() RETURNS TRIGGER AS $$
|
|
201
225
|
DECLARE
|
|
202
226
|
new_group_id BIGINT;
|
package/src/options-parser.ts
CHANGED
|
@@ -240,6 +240,9 @@ export class OptionsParser {
|
|
|
240
240
|
protected parseAppends(appends: Appends, filterParams: any) {
|
|
241
241
|
if (!appends) return filterParams;
|
|
242
242
|
|
|
243
|
+
// sort appends by path length
|
|
244
|
+
appends = lodash.sortBy(appends, (append) => append.split('.').length);
|
|
245
|
+
|
|
243
246
|
/**
|
|
244
247
|
* set include params
|
|
245
248
|
* @param model
|
|
@@ -287,6 +290,25 @@ export class OptionsParser {
|
|
|
287
290
|
// if include from filter, remove fromFilter attribute
|
|
288
291
|
if (existIncludeIndex != -1) {
|
|
289
292
|
delete queryParams['include'][existIncludeIndex]['fromFilter'];
|
|
293
|
+
|
|
294
|
+
// set include attributes to all attributes
|
|
295
|
+
if (
|
|
296
|
+
Array.isArray(queryParams['include'][existIncludeIndex]['attributes']) &&
|
|
297
|
+
queryParams['include'][existIncludeIndex]['attributes'].length == 0
|
|
298
|
+
) {
|
|
299
|
+
queryParams['include'][existIncludeIndex]['attributes'] = {
|
|
300
|
+
include: [],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (
|
|
306
|
+
lastLevel &&
|
|
307
|
+
existIncludeIndex != -1 &&
|
|
308
|
+
lodash.get(queryParams, ['include', existIncludeIndex, 'attributes', 'include'])?.length == 0
|
|
309
|
+
) {
|
|
310
|
+
// if append is last level and association exists, ignore it
|
|
311
|
+
return;
|
|
290
312
|
}
|
|
291
313
|
|
|
292
314
|
// if association not exist, create it
|
package/src/sync-runner.ts
CHANGED
|
@@ -20,7 +20,9 @@ export class SyncRunner {
|
|
|
20
20
|
|
|
21
21
|
if (!parents) {
|
|
22
22
|
throw new Error(
|
|
23
|
-
`Inherit model ${
|
|
23
|
+
`Inherit model ${
|
|
24
|
+
inheritedCollection.name
|
|
25
|
+
} can't be created without parents, parents option is ${lodash
|
|
24
26
|
.castArray(inheritedCollection.options.inherits)
|
|
25
27
|
.join(', ')}`,
|
|
26
28
|
);
|
|
@@ -58,7 +60,7 @@ export class SyncRunner {
|
|
|
58
60
|
const columnDefault = sequenceNameResult[0][0]['column_default'];
|
|
59
61
|
|
|
60
62
|
if (!columnDefault) {
|
|
61
|
-
throw new Error(`Can't find sequence name of ${parent}`);
|
|
63
|
+
throw new Error(`Can't find sequence name of parent collection ${parent.options.name}`);
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
const regex = new RegExp(/nextval\('(.*)'::regclass\)/);
|