remult 3.4.0-next.7 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/internals.js +1 -0
- package/esm/remult-knex/index.js +46 -73
- package/esm/src/data-api.js +42 -31
- package/esm/src/data-providers/sql-database.js +46 -42
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +35 -34
- package/esm/src/filter/walk-filter.js +29 -0
- package/esm/src/remult-proxy.js +6 -6
- package/esm/src/remult3/RepositoryImplementation.js +49 -29
- package/esm/src/sort.js +0 -3
- package/esm/src/valueConverters.js +11 -0
- package/index.d.ts +1 -1
- package/internals.d.ts +1 -0
- package/internals.js +4 -1
- package/package.json +1 -1
- package/remult-knex/index.d.ts +3 -2
- package/remult-knex/index.js +118 -164
- package/src/data-api.d.ts +4 -0
- package/src/data-api.js +51 -48
- package/src/data-providers/sql-database.d.ts +16 -8
- package/src/data-providers/sql-database.js +90 -81
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +8 -3
- package/src/filter/filter-consumer-bridge-to-sql-request.js +40 -47
- package/src/filter/walk-filter.d.ts +3 -0
- package/src/filter/walk-filter.js +56 -0
- package/src/remult-proxy.js +12 -27
- package/src/remult3/RepositoryImplementation.d.ts +4 -2
- package/src/remult3/RepositoryImplementation.js +48 -30
- package/src/remult3/remult3.d.ts +37 -0
- package/src/sort.js +0 -3
- package/src/sql-command.d.ts +5 -0
- package/src/valueConverters.d.ts +4 -1
- package/src/valueConverters.js +13 -0
package/esm/internals.js
CHANGED
|
@@ -4,6 +4,7 @@ export { getRelationInfo, getRelationFieldInfo, } from './src/remult3/relationIn
|
|
|
4
4
|
export { decorateColumnSettings, controllerRefImpl, getControllerRef, fieldOptionsEnricher, } from './src/remult3/RepositoryImplementation.js';
|
|
5
5
|
export { getEntitySettings } from './src/remult3/getEntityRef.js';
|
|
6
6
|
export { __updateEntityBasedOnWhere } from './src/filter/filter-interfaces.js';
|
|
7
|
+
export { omitFilterField, walkFilter } from './src/filter/walk-filter.js';
|
|
7
8
|
export const actionInfo = remultStatic.actionInfo;
|
|
8
9
|
export { isOfType } from './src/isOfType.js';
|
|
9
10
|
export { flags } from './src/remult3/remult3.js';
|
package/esm/remult-knex/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { CustomSqlFilterBuilder, dbNamesOfWithForceSqlExpression, } from '../src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
1
|
+
import { CustomSqlFilterBuilder, dbNamesOfWithForceSqlExpression, escapeLike, LIKE_ESCAPE_SQL, } from '../src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
2
2
|
import { dbNamesOf, isDbReadonly, shouldCreateEntity, shouldNotCreateField, } from '../src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
3
3
|
import { customDatabaseFilterToken, Filter, } from '../src/filter/filter-interfaces.js';
|
|
4
4
|
import { remult as remultContext } from '../src/remult-proxy.js';
|
|
5
5
|
import { getRepository, isAutoIncrement, } from '../src/remult3/RepositoryImplementation.js';
|
|
6
6
|
import { Sort } from '../src/sort.js';
|
|
7
|
-
import { ValueConverters } from '../src/valueConverters.js';
|
|
7
|
+
import { fromDbColumn, toDbColumn, ValueConverters, } from '../src/valueConverters.js';
|
|
8
8
|
import { getRepositoryInternals } from '../src/remult3/repository-internals.js';
|
|
9
|
-
import { groupByImpl, getRowAfterUpdate, } from '../src/data-providers/sql-database.js';
|
|
9
|
+
import { groupByImpl, getRowAfterUpdate, jsonColumnStorageOf, } from '../src/data-providers/sql-database.js';
|
|
10
10
|
export class KnexDataProvider {
|
|
11
11
|
knex;
|
|
12
12
|
constructor(knex) {
|
|
@@ -46,29 +46,14 @@ export class KnexDataProvider {
|
|
|
46
46
|
return r.knex;
|
|
47
47
|
}
|
|
48
48
|
wrapIdentifier = (x) => this.knex.ref(x) + '';
|
|
49
|
-
|
|
49
|
+
get jsonColumnStorage() {
|
|
50
50
|
if (!supportsJsonLoadingAndSaving(this.knex))
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
fromDb: ValueConverters.JsonString.fromDb,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (this.knex.client.config.client === 'mysql2') {
|
|
62
|
-
for (const f of entity.fields.toArray()) {
|
|
63
|
-
if (f.valueConverter.fieldTypeInDb === 'json') {
|
|
64
|
-
//@ts-ignore
|
|
65
|
-
f.valueConverter = {
|
|
66
|
-
...f.valueConverter,
|
|
67
|
-
toDb: ValueConverters.JsonString.toDb,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
51
|
+
return 'text';
|
|
52
|
+
if (this.knex.client.config.client === 'mysql2')
|
|
53
|
+
return 'textOnWrite';
|
|
54
|
+
return 'native';
|
|
55
|
+
}
|
|
56
|
+
getEntityDataProvider(entity) {
|
|
72
57
|
return new KnexEntityDataProvider(entity, this.knex, this.wrapIdentifier, this);
|
|
73
58
|
}
|
|
74
59
|
async transaction(action) {
|
|
@@ -93,7 +78,7 @@ export class KnexDataProvider {
|
|
|
93
78
|
if (!wrapIdentifier)
|
|
94
79
|
wrapIdentifier = (x) => x;
|
|
95
80
|
const repo = getRepository(entity);
|
|
96
|
-
var b = new FilterConsumerBridgeToKnexRequest(await dbNamesOfWithForceSqlExpression(repo.metadata, wrapIdentifier), wrapIdentifier ?? wrapIdentifier);
|
|
81
|
+
var b = new FilterConsumerBridgeToKnexRequest(await dbNamesOfWithForceSqlExpression(repo.metadata, wrapIdentifier), wrapIdentifier ?? wrapIdentifier, jsonColumnStorageOf(repo));
|
|
97
82
|
b._addWhere = false;
|
|
98
83
|
await (await getRepositoryInternals(repo)._translateWhereToFilter(condition)).__applyToConsumer(b);
|
|
99
84
|
let r = await b.resolveWhere();
|
|
@@ -119,7 +104,7 @@ class KnexEntityDataProvider {
|
|
|
119
104
|
async groupBy(options) {
|
|
120
105
|
return groupByImpl(options, await dbNamesOf(this.entity, this.dp.wrapIdentifier), this.dp.createCommand(), false, (limit, offset) => this.knex.client.config.client?.includes('mssql')
|
|
121
106
|
? ' offset ' + offset + ' rows fetch next ' + limit + ' rows only'
|
|
122
|
-
: ' limit ' + limit + ' offset ' + offset);
|
|
107
|
+
: ' limit ' + limit + ' offset ' + offset, this.dp.jsonColumnStorage);
|
|
123
108
|
}
|
|
124
109
|
getEntityFrom(e) {
|
|
125
110
|
if (this.entity.options.sqlExpression)
|
|
@@ -128,7 +113,7 @@ class KnexEntityDataProvider {
|
|
|
128
113
|
}
|
|
129
114
|
async count(where) {
|
|
130
115
|
const e = await this.init();
|
|
131
|
-
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
116
|
+
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
132
117
|
where.__applyToConsumer(br);
|
|
133
118
|
let r = await br.resolveWhere();
|
|
134
119
|
const result = await this.getEntityFrom(e)
|
|
@@ -162,7 +147,7 @@ class KnexEntityDataProvider {
|
|
|
162
147
|
}
|
|
163
148
|
let query = this.getEntityFrom(e).select(cols);
|
|
164
149
|
if (options?.where) {
|
|
165
|
-
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
150
|
+
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
166
151
|
options.where.__applyToConsumer(br);
|
|
167
152
|
let r = await br.resolveWhere();
|
|
168
153
|
query.where((b) => r.forEach((y) => y(b)));
|
|
@@ -188,7 +173,7 @@ class KnexEntityDataProvider {
|
|
|
188
173
|
for (let m in y) {
|
|
189
174
|
let field = colKeys[i++];
|
|
190
175
|
try {
|
|
191
|
-
result[field.key] = field
|
|
176
|
+
result[field.key] = fromDbColumn(field, y[m], this.dp.jsonColumnStorage);
|
|
192
177
|
}
|
|
193
178
|
catch (err) {
|
|
194
179
|
throw new Error('Failed to load from db:' + field.key + '\r\n' + err);
|
|
@@ -212,14 +197,14 @@ class KnexEntityDataProvider {
|
|
|
212
197
|
}
|
|
213
198
|
async update(id, data, options) {
|
|
214
199
|
const e = await this.init();
|
|
215
|
-
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
200
|
+
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
216
201
|
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
217
202
|
let updateObject = {};
|
|
218
203
|
for (const x of this.entity.fields) {
|
|
219
204
|
if (isDbReadonly(x, e)) {
|
|
220
205
|
}
|
|
221
206
|
else if (data[x.key] !== undefined) {
|
|
222
|
-
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key]);
|
|
207
|
+
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key], this.dp.jsonColumnStorage);
|
|
223
208
|
if (v !== undefined) {
|
|
224
209
|
let key = await e.$dbNameOf(x);
|
|
225
210
|
updateObject[key] = v;
|
|
@@ -238,7 +223,7 @@ class KnexEntityDataProvider {
|
|
|
238
223
|
if (ids.length === 0)
|
|
239
224
|
return;
|
|
240
225
|
const e = await this.init();
|
|
241
|
-
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
226
|
+
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
242
227
|
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
|
|
243
228
|
let where = await f.resolveWhere();
|
|
244
229
|
await this.getEntityFrom(e)
|
|
@@ -257,7 +242,7 @@ class KnexEntityDataProvider {
|
|
|
257
242
|
return result;
|
|
258
243
|
}
|
|
259
244
|
const e = await this.init();
|
|
260
|
-
const batches = splitByMaxParameters(data, (row) => countKnexInsertBindParameters(this.entity, e, row), knexMaxParametersInOneSqlStatement(this.knex));
|
|
245
|
+
const batches = splitByMaxParameters(data, (row) => countKnexInsertBindParameters(this.entity, e, row, this.dp.jsonColumnStorage), knexMaxParametersInOneSqlStatement(this.knex));
|
|
261
246
|
const result = [];
|
|
262
247
|
for (const batch of batches)
|
|
263
248
|
result.push(...(await this.insertBatch(batch, e, options)));
|
|
@@ -268,15 +253,14 @@ class KnexEntityDataProvider {
|
|
|
268
253
|
for (const x of this.entity.fields) {
|
|
269
254
|
if (isDbReadonly(x, e))
|
|
270
255
|
continue;
|
|
271
|
-
if (batch.some((row) => translateValueAndHandleArrayAndHandleArray(x, row[x.key]) !=
|
|
272
|
-
undefined))
|
|
256
|
+
if (batch.some((row) => translateValueAndHandleArrayAndHandleArray(x, row[x.key], this.dp.jsonColumnStorage) != undefined))
|
|
273
257
|
cols.push(x);
|
|
274
258
|
}
|
|
275
259
|
const insertObjects = [];
|
|
276
260
|
for (const row of batch) {
|
|
277
261
|
const insertObject = {};
|
|
278
262
|
for (const x of cols) {
|
|
279
|
-
const v = translateValueAndHandleArrayAndHandleArray(x, row[x.key]);
|
|
263
|
+
const v = translateValueAndHandleArrayAndHandleArray(x, row[x.key], this.dp.jsonColumnStorage);
|
|
280
264
|
const key = await e.$dbNameOf(x);
|
|
281
265
|
if (v != undefined)
|
|
282
266
|
insertObject[key] = v;
|
|
@@ -291,9 +275,7 @@ class KnexEntityDataProvider {
|
|
|
291
275
|
const wantRows = options?.select !== 'none';
|
|
292
276
|
if (isAutoIncrement(this.entity.idMetadata.field) && wantRows) {
|
|
293
277
|
const result = await insert.returning(this.entity.idMetadata.field.dbName);
|
|
294
|
-
const ids = result.map((r) => r && typeof r === 'object'
|
|
295
|
-
? r[this.entity.idMetadata.field.dbName]
|
|
296
|
-
: r);
|
|
278
|
+
const ids = result.map((r) => r && typeof r === 'object' ? r[this.entity.idMetadata.field.dbName] : r);
|
|
297
279
|
return this.loadRowsByIds(ids);
|
|
298
280
|
}
|
|
299
281
|
await insert;
|
|
@@ -321,7 +303,7 @@ class KnexEntityDataProvider {
|
|
|
321
303
|
if (isDbReadonly(x, e)) {
|
|
322
304
|
}
|
|
323
305
|
else {
|
|
324
|
-
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key]);
|
|
306
|
+
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key], this.dp.jsonColumnStorage);
|
|
325
307
|
if (v != undefined) {
|
|
326
308
|
let key = await e.$dbNameOf(x);
|
|
327
309
|
insertObject[key] = v;
|
|
@@ -357,6 +339,7 @@ class KnexEntityDataProvider {
|
|
|
357
339
|
class FilterConsumerBridgeToKnexRequest {
|
|
358
340
|
innerNameProvider;
|
|
359
341
|
rawSqlWrapIdentifier;
|
|
342
|
+
jsonColumnStorage;
|
|
360
343
|
_addWhere = true;
|
|
361
344
|
promises = [];
|
|
362
345
|
result = [];
|
|
@@ -370,9 +353,10 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
370
353
|
}
|
|
371
354
|
return this.result;
|
|
372
355
|
}
|
|
373
|
-
constructor(innerNameProvider, rawSqlWrapIdentifier) {
|
|
356
|
+
constructor(innerNameProvider, rawSqlWrapIdentifier, jsonColumnStorage) {
|
|
374
357
|
this.innerNameProvider = innerNameProvider;
|
|
375
358
|
this.rawSqlWrapIdentifier = rawSqlWrapIdentifier;
|
|
359
|
+
this.jsonColumnStorage = jsonColumnStorage;
|
|
376
360
|
}
|
|
377
361
|
custom(key, customItem) {
|
|
378
362
|
throw new Error('Custom filter should be translated before it gets here');
|
|
@@ -381,7 +365,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
381
365
|
this.promises.push((async () => {
|
|
382
366
|
const result = [];
|
|
383
367
|
for (const element of orElements) {
|
|
384
|
-
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier);
|
|
368
|
+
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier, this.jsonColumnStorage);
|
|
385
369
|
f._addWhere = false;
|
|
386
370
|
element.__applyToConsumer(f);
|
|
387
371
|
let where = await f.resolveWhere();
|
|
@@ -402,7 +386,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
402
386
|
}
|
|
403
387
|
not(element) {
|
|
404
388
|
this.promises.push((async () => {
|
|
405
|
-
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier);
|
|
389
|
+
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier, this.jsonColumnStorage);
|
|
406
390
|
f._addWhere = false;
|
|
407
391
|
element.__applyToConsumer(f);
|
|
408
392
|
let where = await f.resolveWhere();
|
|
@@ -424,7 +408,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
424
408
|
this.result.push((b) => b.whereNotNull(this.innerNameProvider.$dbNameOf(col)));
|
|
425
409
|
}
|
|
426
410
|
isIn(col, val) {
|
|
427
|
-
this.result.push((knex) => knex.whereIn(this.innerNameProvider.$dbNameOf(col), val.map((x) => translateValueAndHandleArrayAndHandleArray(col, x))));
|
|
411
|
+
this.result.push((knex) => knex.whereIn(this.innerNameProvider.$dbNameOf(col), val.map((x) => translateValueAndHandleArrayAndHandleArray(col, x, this.jsonColumnStorage))));
|
|
428
412
|
}
|
|
429
413
|
isEqualTo(col, val) {
|
|
430
414
|
this.add(col, val, '=');
|
|
@@ -445,39 +429,27 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
445
429
|
this.add(col, val, '<');
|
|
446
430
|
}
|
|
447
431
|
containsCaseInsensitive(col, val) {
|
|
448
|
-
this.
|
|
449
|
-
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
450
|
-
") like lower ('%" +
|
|
451
|
-
val.replace(/'/g, "''") +
|
|
452
|
-
"%')"));
|
|
453
|
-
this.promises.push((async () => { })());
|
|
432
|
+
this.like(col, '%' + escapeLike(val) + '%');
|
|
454
433
|
}
|
|
455
434
|
notContainsCaseInsensitive(col, val) {
|
|
456
|
-
this.
|
|
457
|
-
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
458
|
-
") like lower ('%" +
|
|
459
|
-
val.replace(/'/g, "''") +
|
|
460
|
-
"%')"));
|
|
461
|
-
this.promises.push((async () => { })());
|
|
435
|
+
this.like(col, '%' + escapeLike(val) + '%', 'not ');
|
|
462
436
|
}
|
|
463
437
|
startsWithCaseInsensitive(col, val) {
|
|
464
|
-
this.
|
|
465
|
-
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
466
|
-
") like lower ('" +
|
|
467
|
-
val.replace(/'/g, "''") +
|
|
468
|
-
"%')"));
|
|
469
|
-
this.promises.push((async () => { })());
|
|
438
|
+
this.like(col, escapeLike(val) + '%');
|
|
470
439
|
}
|
|
471
440
|
endsWithCaseInsensitive(col, val) {
|
|
472
|
-
this.
|
|
441
|
+
this.like(col, '%' + escapeLike(val));
|
|
442
|
+
}
|
|
443
|
+
like(col, pattern, not = '') {
|
|
444
|
+
this.result.push((b) => b.whereRaw(not +
|
|
445
|
+
'lower (' +
|
|
473
446
|
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
"')"));
|
|
447
|
+
') like lower (?)' +
|
|
448
|
+
LIKE_ESCAPE_SQL, [pattern]));
|
|
477
449
|
this.promises.push((async () => { })());
|
|
478
450
|
}
|
|
479
451
|
add(col, val, operator) {
|
|
480
|
-
this.result.push((b) => b.where(this.innerNameProvider.$dbNameOf(col), operator, translateValueAndHandleArrayAndHandleArray(col, val)));
|
|
452
|
+
this.result.push((b) => b.where(this.innerNameProvider.$dbNameOf(col), operator, translateValueAndHandleArrayAndHandleArray(col, val, this.jsonColumnStorage)));
|
|
481
453
|
}
|
|
482
454
|
databaseCustom(databaseCustom) {
|
|
483
455
|
this.promises.push((async () => {
|
|
@@ -486,7 +458,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
486
458
|
}
|
|
487
459
|
if (databaseCustom?.buildSql) {
|
|
488
460
|
let r = new KnexCommandHelper();
|
|
489
|
-
const item = new CustomSqlFilterBuilder(r, this.rawSqlWrapIdentifier);
|
|
461
|
+
const item = new CustomSqlFilterBuilder(r, this.rawSqlWrapIdentifier, this.jsonColumnStorage);
|
|
490
462
|
let sql = await databaseCustom.buildSql(item);
|
|
491
463
|
if (typeof sql !== 'string')
|
|
492
464
|
sql = item.sql;
|
|
@@ -691,18 +663,19 @@ function splitByMaxParameters(items, parametersInItem, maxParameters) {
|
|
|
691
663
|
batches.push(batch);
|
|
692
664
|
return batches;
|
|
693
665
|
}
|
|
694
|
-
function countKnexInsertBindParameters(entity, e, row) {
|
|
666
|
+
function countKnexInsertBindParameters(entity, e, row, storage) {
|
|
695
667
|
let n = 0;
|
|
696
668
|
for (const x of entity.fields) {
|
|
697
669
|
if (isDbReadonly(x, e))
|
|
698
670
|
continue;
|
|
699
|
-
if (translateValueAndHandleArrayAndHandleArray(x, row[x.key]) !=
|
|
671
|
+
if (translateValueAndHandleArrayAndHandleArray(x, row[x.key], storage) !=
|
|
672
|
+
undefined)
|
|
700
673
|
n++;
|
|
701
674
|
}
|
|
702
675
|
return n;
|
|
703
676
|
}
|
|
704
|
-
function translateValueAndHandleArrayAndHandleArray(field, val) {
|
|
705
|
-
let result = field
|
|
677
|
+
function translateValueAndHandleArrayAndHandleArray(field, val, storage) {
|
|
678
|
+
let result = toDbColumn(field, val, storage);
|
|
706
679
|
if (Array.isArray(result))
|
|
707
680
|
return JSON.stringify(result);
|
|
708
681
|
return result;
|
package/esm/src/data-api.js
CHANGED
|
@@ -2,6 +2,7 @@ import { doTransaction } from './context.js';
|
|
|
2
2
|
import { EntityError } from './data-interfaces.js';
|
|
3
3
|
import { findOptionsToJson, liveQueryAction, } from './data-providers/rest-data-provider.js';
|
|
4
4
|
import { Filter, buildFilterFromRequestParameters, customUrlToken, } from './filter/filter-interfaces.js';
|
|
5
|
+
import { omitFilterField, walkFilter } from './filter/walk-filter.js';
|
|
5
6
|
import { getRelationFieldInfo } from './remult3/relationInfoMember.js';
|
|
6
7
|
import { GroupByCountMember, GroupByForApiKey, } from './remult3/remult3.js';
|
|
7
8
|
import { ForbiddenError } from './server-action.js';
|
|
@@ -27,10 +28,7 @@ export class DataApi {
|
|
|
27
28
|
return this.getArray(res, req, undefined);
|
|
28
29
|
}
|
|
29
30
|
catch (err) {
|
|
30
|
-
|
|
31
|
-
res.forbidden(err.message);
|
|
32
|
-
else
|
|
33
|
-
res.error(err, this.repository.metadata);
|
|
31
|
+
this.handleError(res, err);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
async httpPost(res, req, body, serializeContext) {
|
|
@@ -76,10 +74,7 @@ export class DataApi {
|
|
|
76
74
|
}
|
|
77
75
|
}
|
|
78
76
|
catch (err) {
|
|
79
|
-
|
|
80
|
-
res.forbidden(err.message);
|
|
81
|
-
else
|
|
82
|
-
res.error(err, this.repository.metadata);
|
|
77
|
+
this.handleError(res, err);
|
|
83
78
|
}
|
|
84
79
|
}
|
|
85
80
|
async upsertMany(response, request, body) {
|
|
@@ -130,10 +125,7 @@ export class DataApi {
|
|
|
130
125
|
});
|
|
131
126
|
}
|
|
132
127
|
catch (err) {
|
|
133
|
-
|
|
134
|
-
response.forbidden();
|
|
135
|
-
else
|
|
136
|
-
response.error(err, this.repository.metadata);
|
|
128
|
+
this.handleError(response, err);
|
|
137
129
|
}
|
|
138
130
|
}
|
|
139
131
|
static defaultGetLimit = 0;
|
|
@@ -155,7 +147,7 @@ export class DataApi {
|
|
|
155
147
|
});
|
|
156
148
|
}
|
|
157
149
|
catch (err) {
|
|
158
|
-
|
|
150
|
+
this.handleError(response, err);
|
|
159
151
|
}
|
|
160
152
|
}
|
|
161
153
|
async deleteMany(response, request, body) {
|
|
@@ -175,7 +167,7 @@ export class DataApi {
|
|
|
175
167
|
});
|
|
176
168
|
}
|
|
177
169
|
catch (err) {
|
|
178
|
-
|
|
170
|
+
this.handleError(response, err);
|
|
179
171
|
}
|
|
180
172
|
}
|
|
181
173
|
async prepareWhereForManyOperation(body, request) {
|
|
@@ -196,7 +188,7 @@ export class DataApi {
|
|
|
196
188
|
for (const element of body?.orderBy) {
|
|
197
189
|
const direction = element.isDescending ? 'desc' : 'asc';
|
|
198
190
|
if (element.operation !== 'count' &&
|
|
199
|
-
!this.
|
|
191
|
+
!this.fieldIsInApi(element.field))
|
|
200
192
|
continue;
|
|
201
193
|
switch (element.operation) {
|
|
202
194
|
case undefined:
|
|
@@ -214,9 +206,9 @@ export class DataApi {
|
|
|
214
206
|
}
|
|
215
207
|
}
|
|
216
208
|
}
|
|
217
|
-
const group = body?.groupBy
|
|
218
|
-
const min = body?.min
|
|
219
|
-
const max = body?.max
|
|
209
|
+
const group = this.fieldsInApi(body?.groupBy);
|
|
210
|
+
const min = this.fieldsInApi(body?.min);
|
|
211
|
+
const max = this.fieldsInApi(body?.max);
|
|
220
212
|
let result = await this.repository.groupBy({
|
|
221
213
|
where: findOptions.where,
|
|
222
214
|
limit: findOptions.limit,
|
|
@@ -224,11 +216,11 @@ export class DataApi {
|
|
|
224
216
|
//@ts-expect-error internal key
|
|
225
217
|
[GroupByForApiKey]: true,
|
|
226
218
|
group,
|
|
227
|
-
sum: body?.sum
|
|
228
|
-
avg: body?.avg
|
|
219
|
+
sum: this.fieldsInApi(body?.sum),
|
|
220
|
+
avg: this.fieldsInApi(body?.avg),
|
|
229
221
|
min,
|
|
230
222
|
max,
|
|
231
|
-
distinctCount: body?.distinctCount
|
|
223
|
+
distinctCount: this.fieldsInApi(body?.distinctCount),
|
|
232
224
|
orderBy: orderBy,
|
|
233
225
|
});
|
|
234
226
|
if (group)
|
|
@@ -269,7 +261,7 @@ export class DataApi {
|
|
|
269
261
|
let sort = request.get('_sort');
|
|
270
262
|
if (sort != undefined) {
|
|
271
263
|
let dir = request.get('_order');
|
|
272
|
-
findOptions.orderBy = determineSort(sort, dir);
|
|
264
|
+
findOptions.orderBy = this.omitFieldsHiddenFromApi(determineSort(sort, dir));
|
|
273
265
|
}
|
|
274
266
|
let limit = +request.get('_limit');
|
|
275
267
|
if (!limit && DataApi.defaultGetLimit)
|
|
@@ -341,10 +333,7 @@ export class DataApi {
|
|
|
341
333
|
response.success(r);
|
|
342
334
|
}
|
|
343
335
|
catch (err) {
|
|
344
|
-
|
|
345
|
-
response.forbidden(err.message);
|
|
346
|
-
else
|
|
347
|
-
response.error(err, this.repository.metadata);
|
|
336
|
+
this.handleError(response, err);
|
|
348
337
|
}
|
|
349
338
|
}
|
|
350
339
|
async liveQuery(response, request, body, serializeContext, queryChannel) {
|
|
@@ -368,10 +357,7 @@ export class DataApi {
|
|
|
368
357
|
response.success(r.r);
|
|
369
358
|
}
|
|
370
359
|
catch (err) {
|
|
371
|
-
|
|
372
|
-
response.forbidden();
|
|
373
|
-
else
|
|
374
|
-
response.error(err, this.repository.metadata);
|
|
360
|
+
this.handleError(response, err);
|
|
375
361
|
}
|
|
376
362
|
}
|
|
377
363
|
async buildWhere(request, body) {
|
|
@@ -396,6 +382,7 @@ export class DataApi {
|
|
|
396
382
|
return result;
|
|
397
383
|
},
|
|
398
384
|
});
|
|
385
|
+
f = this.omitFieldsHiddenFromApi(f);
|
|
399
386
|
if (this.repository.metadata.options.apiPreprocessFilter) {
|
|
400
387
|
f = await this.repository.metadata.options.apiPreprocessFilter(f, {
|
|
401
388
|
metadata: this.repository.metadata,
|
|
@@ -408,6 +395,30 @@ export class DataApi {
|
|
|
408
395
|
}
|
|
409
396
|
return { $and: where };
|
|
410
397
|
}
|
|
398
|
+
// A hidden field is not on the api, same as a field that is not on the entity.
|
|
399
|
+
fieldIsInApi(key) {
|
|
400
|
+
return this.repository.fields.find(key)?.includedInApi() === true;
|
|
401
|
+
}
|
|
402
|
+
fieldsInApi(keys) {
|
|
403
|
+
return keys?.filter((key) => this.fieldIsInApi(key));
|
|
404
|
+
}
|
|
405
|
+
// Drop a field the api hides. Same as a field that is not on the entity:
|
|
406
|
+
// refusing it would confirm the name exists.
|
|
407
|
+
omitFieldsHiddenFromApi(filterOrSort) {
|
|
408
|
+
const fields = this.repository.metadata.fields;
|
|
409
|
+
return walkFilter(filterOrSort, function (key, value) {
|
|
410
|
+
if (!key.startsWith(customUrlToken) &&
|
|
411
|
+
fields.find(key)?.includedInApi() === false)
|
|
412
|
+
return omitFilterField;
|
|
413
|
+
return value;
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
handleError(response, err) {
|
|
417
|
+
if (err.isForbiddenError)
|
|
418
|
+
response.forbidden(err.message);
|
|
419
|
+
else
|
|
420
|
+
response.error(err, this.repository.metadata);
|
|
421
|
+
}
|
|
411
422
|
async doOnId(response, id, what) {
|
|
412
423
|
try {
|
|
413
424
|
var where = [
|
|
@@ -467,7 +478,7 @@ export class DataApi {
|
|
|
467
478
|
});
|
|
468
479
|
}
|
|
469
480
|
catch (err) {
|
|
470
|
-
|
|
481
|
+
this.handleError(response, err);
|
|
471
482
|
}
|
|
472
483
|
}
|
|
473
484
|
async actualUpdate(row, body, req, doNotSelect) {
|