remult 0.13.11 → 0.13.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/readme.md +11 -8
- package/src/context.js +12 -6
- package/src/data-interfaces.d.ts +1 -0
- package/src/filter/filter-interfaces.js +48 -22
- package/src/remult3/RepositoryImplementation.d.ts +2 -4
- package/src/remult3/RepositoryImplementation.js +162 -137
- package/src/shared-tests/db-tests.db-tests.js +18 -0
- package/src/tests/frontend-database-tests-setup.spec.d.ts +4 -0
- package/src/tests/frontend-database-tests-setup.spec.js +101 -0
- package/src/tests/testHelper.spec.d.ts +34 -0
- package/src/tests/testHelper.spec.js +369 -0
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -99,23 +99,26 @@ static async increasePriceOfTofu(priceIncrease: number, remult?: Remult) {
|
|
|
99
99
|
## :ballot_box_with_check: Data validation and constraints - defined once
|
|
100
100
|
|
|
101
101
|
```ts
|
|
102
|
-
import { Entity, EntityBase,
|
|
103
|
-
import { Min } from 'class-validator';
|
|
102
|
+
import { Entity, EntityBase, Fields } from 'remult';
|
|
104
103
|
|
|
105
104
|
@Entity('products', {
|
|
106
105
|
allowApiCrud: true
|
|
107
106
|
})
|
|
108
107
|
export class Product extends EntityBase {
|
|
109
108
|
@Fields.string<Product>({
|
|
110
|
-
validate:
|
|
111
|
-
if (
|
|
112
|
-
|
|
109
|
+
validate: product => {
|
|
110
|
+
if (product.name.trim().length == 0)
|
|
111
|
+
product.$.name.error = 'required';
|
|
113
112
|
}
|
|
114
113
|
})
|
|
115
114
|
name = '';
|
|
116
115
|
|
|
117
|
-
@Fields.number(
|
|
118
|
-
|
|
116
|
+
@Fields.number({
|
|
117
|
+
validate: (_, field) => {
|
|
118
|
+
if (field.value < 0)
|
|
119
|
+
field.error = "must not be less than 0";
|
|
120
|
+
}
|
|
121
|
+
})
|
|
119
122
|
unitPrice = 0;
|
|
120
123
|
}
|
|
121
124
|
```
|
|
@@ -136,7 +139,7 @@ catch {
|
|
|
136
139
|
```sh
|
|
137
140
|
> curl -d "{""unitPrice"":-1}" -H "Content-Type: application/json" -X POST http://localhost:3001/api/products
|
|
138
141
|
|
|
139
|
-
{"modelState":{"unitPrice":"
|
|
142
|
+
{"modelState":{"unitPrice":"must not be less than 0","name":"required"},"message":"Name: required"}
|
|
140
143
|
```
|
|
141
144
|
|
|
142
145
|
## :lock: Secure the API with fine-grained authorization
|
package/src/context.js
CHANGED
|
@@ -94,14 +94,14 @@ function toPromise(p) {
|
|
|
94
94
|
}
|
|
95
95
|
exports.toPromise = toPromise;
|
|
96
96
|
function processHttpException(ex) {
|
|
97
|
-
var _a, _b;
|
|
97
|
+
var _a, _b, _c;
|
|
98
98
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
99
|
-
var z, error, result;
|
|
100
|
-
return tslib_1.__generator(this, function (
|
|
101
|
-
switch (
|
|
99
|
+
var z, error, httpStatusCode, result;
|
|
100
|
+
return tslib_1.__generator(this, function (_d) {
|
|
101
|
+
switch (_d.label) {
|
|
102
102
|
case 0: return [4 /*yield*/, ex];
|
|
103
103
|
case 1:
|
|
104
|
-
z =
|
|
104
|
+
z = _d.sent();
|
|
105
105
|
if (z.error)
|
|
106
106
|
error = z.error;
|
|
107
107
|
else if (z.isAxiosError) {
|
|
@@ -116,9 +116,15 @@ function processHttpException(ex) {
|
|
|
116
116
|
error = "Network Error";
|
|
117
117
|
if (typeof error === 'string') {
|
|
118
118
|
error = {
|
|
119
|
-
message: error
|
|
119
|
+
message: error,
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
+
httpStatusCode = z.status;
|
|
123
|
+
if (httpStatusCode === undefined)
|
|
124
|
+
httpStatusCode = (_c = z.response) === null || _c === void 0 ? void 0 : _c.status;
|
|
125
|
+
if (httpStatusCode !== undefined && httpStatusCode !== null) {
|
|
126
|
+
error.httpStatusCode = httpStatusCode;
|
|
127
|
+
}
|
|
122
128
|
result = Object.assign(error, {
|
|
123
129
|
// exception: ex disabled for now because JSON.stringify crashed with this
|
|
124
130
|
});
|
package/src/data-interfaces.d.ts
CHANGED
|
@@ -397,11 +397,12 @@ function entityFilterToJson(entityDefs, where) {
|
|
|
397
397
|
}
|
|
398
398
|
exports.entityFilterToJson = entityFilterToJson;
|
|
399
399
|
function buildFilterFromRequestParameters(entity, filterInfo) {
|
|
400
|
-
var
|
|
400
|
+
var _a;
|
|
401
401
|
var where = [];
|
|
402
402
|
tslib_1.__spread(entity.fields).forEach(function (col) {
|
|
403
403
|
var _a, _b;
|
|
404
404
|
function addFilter(operation, theFilter, jsonArray, asString) {
|
|
405
|
+
var e_3, _a;
|
|
405
406
|
if (jsonArray === void 0) { jsonArray = false; }
|
|
406
407
|
if (asString === void 0) { asString = false; }
|
|
407
408
|
var val = filterInfo.get(col.key + operation);
|
|
@@ -430,8 +431,26 @@ function buildFilterFromRequestParameters(entity, filterInfo) {
|
|
|
430
431
|
addFilter_1(v);
|
|
431
432
|
});
|
|
432
433
|
}
|
|
433
|
-
else
|
|
434
|
-
|
|
434
|
+
else {
|
|
435
|
+
if (jsonArray) {
|
|
436
|
+
if (typeof val === 'string')
|
|
437
|
+
val = JSON.parse(val);
|
|
438
|
+
}
|
|
439
|
+
var array = separateArrayFromInnerArray(val);
|
|
440
|
+
try {
|
|
441
|
+
for (var array_1 = tslib_1.__values(array), array_1_1 = array_1.next(); !array_1_1.done; array_1_1 = array_1.next()) {
|
|
442
|
+
var x = array_1_1.value;
|
|
443
|
+
addFilter_1(x);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
447
|
+
finally {
|
|
448
|
+
try {
|
|
449
|
+
if (array_1_1 && !array_1_1.done && (_a = array_1.return)) _a.call(array_1);
|
|
450
|
+
}
|
|
451
|
+
finally { if (e_3) throw e_3.error; }
|
|
452
|
+
}
|
|
453
|
+
}
|
|
435
454
|
}
|
|
436
455
|
}
|
|
437
456
|
var c = new filterHelper(col);
|
|
@@ -460,6 +479,29 @@ function buildFilterFromRequestParameters(entity, filterInfo) {
|
|
|
460
479
|
});
|
|
461
480
|
var val = filterInfo.get('OR');
|
|
462
481
|
if (val) {
|
|
482
|
+
var array = separateArrayFromInnerArray(val);
|
|
483
|
+
where.push({
|
|
484
|
+
$and: array.map(function (v) { return ({
|
|
485
|
+
$or: v.map(function (x) {
|
|
486
|
+
return buildFilterFromRequestParameters(entity, { get: function (key) { return x[key]; } });
|
|
487
|
+
})
|
|
488
|
+
}); })
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
for (var key in entity.entityType) {
|
|
492
|
+
var element = entity.entityType[key];
|
|
493
|
+
if (element && element.customFilterInfo && element.customFilterInfo.customFilterTranslator) {
|
|
494
|
+
var custom = filterInfo.get(exports.customUrlToken + key);
|
|
495
|
+
if (custom !== undefined) {
|
|
496
|
+
where.push((_a = {}, _a[exports.customUrlToken + key] = custom, _a));
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return { $and: where };
|
|
501
|
+
function separateArrayFromInnerArray(val) {
|
|
502
|
+
var e_4, _a;
|
|
503
|
+
if (!Array.isArray(val))
|
|
504
|
+
return [val];
|
|
463
505
|
var nonArray = [], array = [];
|
|
464
506
|
try {
|
|
465
507
|
for (var val_1 = tslib_1.__values(val), val_1_1 = val_1.next(); !val_1_1.done; val_1_1 = val_1.next()) {
|
|
@@ -471,32 +513,16 @@ function buildFilterFromRequestParameters(entity, filterInfo) {
|
|
|
471
513
|
nonArray.push(v);
|
|
472
514
|
}
|
|
473
515
|
}
|
|
474
|
-
catch (
|
|
516
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
475
517
|
finally {
|
|
476
518
|
try {
|
|
477
519
|
if (val_1_1 && !val_1_1.done && (_a = val_1.return)) _a.call(val_1);
|
|
478
520
|
}
|
|
479
|
-
finally { if (
|
|
521
|
+
finally { if (e_4) throw e_4.error; }
|
|
480
522
|
}
|
|
481
523
|
array.push(nonArray);
|
|
482
|
-
|
|
483
|
-
$and: array.map(function (v) { return ({
|
|
484
|
-
$or: v.map(function (x) {
|
|
485
|
-
return buildFilterFromRequestParameters(entity, { get: function (key) { return x[key]; } });
|
|
486
|
-
})
|
|
487
|
-
}); })
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
for (var key in entity.entityType) {
|
|
491
|
-
var element = entity.entityType[key];
|
|
492
|
-
if (element && element.customFilterInfo && element.customFilterInfo.customFilterTranslator) {
|
|
493
|
-
var custom = filterInfo.get(exports.customUrlToken + key);
|
|
494
|
-
if (custom !== undefined) {
|
|
495
|
-
where.push((_b = {}, _b[exports.customUrlToken + key] = custom, _b));
|
|
496
|
-
}
|
|
497
|
-
}
|
|
524
|
+
return array;
|
|
498
525
|
}
|
|
499
|
-
return { $and: where };
|
|
500
526
|
}
|
|
501
527
|
exports.buildFilterFromRequestParameters = buildFilterFromRequestParameters;
|
|
502
528
|
var customTranslator = /** @class */ (function () {
|
|
@@ -38,6 +38,7 @@ export declare class RepositoryImplementation<entityType> implements Repository<
|
|
|
38
38
|
} ? number : entityType extends {
|
|
39
39
|
id: string;
|
|
40
40
|
} ? string : (string | number)), entity: Partial<OmitEB<entityType>>): Promise<entityType>;
|
|
41
|
+
private getRefForExistingRow;
|
|
41
42
|
save(item: Partial<OmitEB<entityType>>[]): Promise<entityType[]>;
|
|
42
43
|
save(item: Partial<OmitEB<entityType>>): Promise<entityType>;
|
|
43
44
|
find(options: FindOptions<entityType>): Promise<entityType[]>;
|
|
@@ -114,7 +115,7 @@ export declare class rowHelperImplementation<T> extends rowHelperBase<T> impleme
|
|
|
114
115
|
private getIdFilter;
|
|
115
116
|
delete(): Promise<void>;
|
|
116
117
|
loadDataFrom(data: any, loadItems?: FieldMetadata[]): Promise<void>;
|
|
117
|
-
|
|
118
|
+
id: any;
|
|
118
119
|
getOriginalId(): any;
|
|
119
120
|
private calcServerExpression;
|
|
120
121
|
isNew(): boolean;
|
|
@@ -206,9 +207,6 @@ declare class EntityFullInfo<T> implements EntityMetadata<T> {
|
|
|
206
207
|
caption: string;
|
|
207
208
|
}
|
|
208
209
|
export declare function FieldType<valueType = any>(...options: (FieldOptions<any, valueType> | ((options: FieldOptions<any, valueType>, remult: Remult) => void))[]): (target: any) => any;
|
|
209
|
-
export declare const Fields1: {
|
|
210
|
-
String: () => (a: any) => void;
|
|
211
|
-
};
|
|
212
210
|
export declare class Fields {
|
|
213
211
|
static object<entityType = any, valueType = any>(...options: (FieldOptions<entityType, valueType> | ((options: FieldOptions<entityType, valueType>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
214
212
|
static dateOnly<entityType = any>(...options: (FieldOptions<entityType, Date> | ((options: FieldOptions<entityType, Date>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|