prostgles-server 2.0.145 → 2.0.146
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/dist/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder.js +36 -3
- package/dist/DboBuilder.js.map +1 -1
- package/dist/QueryBuilder.d.ts +15 -3
- package/dist/QueryBuilder.d.ts.map +1 -1
- package/dist/QueryBuilder.js +63 -26
- package/dist/QueryBuilder.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +5 -0
- package/dist/utils.js.map +1 -1
- package/lib/DboBuilder.ts +36 -6
- package/lib/QueryBuilder.ts +76 -27
- package/lib/utils.ts +6 -1
- package/package.json +5 -7
- package/tests/client/PID.txt +1 -1
- package/tests/isomorphic_queries.js +21 -0
- package/tests/isomorphic_queries.ts +23 -0
- package/tests/server/package-lock.json +9 -13
package/lib/QueryBuilder.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { pgp, Filter, LocalParams, isPlainObject, TableHandler, ViewHandler, postgresToTsType } from "./DboBuilder";
|
|
8
8
|
import { TableRule, flat } from "./Prostgles";
|
|
9
9
|
import { SelectParamsBasic as SelectParams, isEmpty, FieldFilter, asName, TextFilter_FullTextSearchFilterKeys, TS_PG_Types, ColumnInfo, PG_COLUMN_UDT_DATA_TYPE } from "prostgles-types";
|
|
10
|
-
import { get } from "./utils";
|
|
10
|
+
import { get, isObject } from "./utils";
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
export type SelectItem = {
|
|
@@ -47,6 +47,65 @@ export const asNameAlias = (field: string, tableAlias?: string) => {
|
|
|
47
47
|
return result;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export const parseFunctionObject = (funcData: any): { funcName: string; args: any[] } => {
|
|
51
|
+
const makeErr = (msg: string) => `Function not specified correctly. Expecting { $funcName: ["columnName",...] } object but got: ${JSON.stringify(funcData)} \n ${msg}`
|
|
52
|
+
if(!isObject(funcData)) throw makeErr("");
|
|
53
|
+
const keys = Object.keys(funcData);
|
|
54
|
+
if(keys.length !== 1) throw makeErr("");
|
|
55
|
+
const funcName = keys[0];
|
|
56
|
+
const args = funcData[funcName];
|
|
57
|
+
if(!args || !Array.isArray(args)){
|
|
58
|
+
throw makeErr("Arguments missing or invalid");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { funcName, args };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const parseFunction = (funcData: { func: string | FunctionSpec, args: any[], functions: FunctionSpec[]; allowedFields: string[]; }): FunctionSpec => {
|
|
65
|
+
const { func, args, functions, allowedFields } = funcData;
|
|
66
|
+
|
|
67
|
+
/* Function is computed column. No checks needed */
|
|
68
|
+
if(typeof func !== "string"){
|
|
69
|
+
const computedCol = COMPUTED_FIELDS.find(c => c.name === func.name);
|
|
70
|
+
if(!computedCol) throw `Unexpected function: computed column spec not found for ${JSON.stringify(func.name)}`;
|
|
71
|
+
return func;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const funcName = func;
|
|
75
|
+
const makeErr = (msg: string): string => {
|
|
76
|
+
return `Issue with function ${JSON.stringify({ [funcName]: args })}: \n${msg}`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Find function */
|
|
80
|
+
const funcDef = functions.find(f => f.name === funcName);
|
|
81
|
+
|
|
82
|
+
if(!funcDef) {
|
|
83
|
+
const sf = functions.filter(f => f.name.toLowerCase().slice(1).startsWith(funcName.toLowerCase())).sort((a, b) => (a.name.length - b.name.length));
|
|
84
|
+
const hint = (sf.length? `. \n Maybe you meant: \n | ${sf.map(s => s.name + " " + (s.description || "")).join(" \n | ")} ?` : "");
|
|
85
|
+
throw "\n Function " + funcName + " does not exist or is not allowed " + hint;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Validate fields */
|
|
89
|
+
const fields = funcDef.getFields(args);
|
|
90
|
+
if(fields !== "*"){
|
|
91
|
+
fields.forEach(fieldKey => {
|
|
92
|
+
if(typeof fieldKey !== "string" || !allowedFields.includes(fieldKey)) {
|
|
93
|
+
throw makeErr(`getFields() => field name ${JSON.stringify(fieldKey)} is invalid or disallowed`)
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
if((funcDef.minCols ?? 0) > fields.length){
|
|
97
|
+
throw makeErr(`Less columns provided than necessary (minCols=${funcDef.minCols})`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if(funcDef.numArgs && funcDef.minCols !== 0 && fields !== "*" && Array.isArray(fields) && !fields.length) {
|
|
102
|
+
throw `\n Function "${funcDef.name}" expects at least a field name but has not been provided with one`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return funcDef;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
50
109
|
type GetQueryArgs = {
|
|
51
110
|
allColumns: ColumnInfo[];
|
|
52
111
|
allowedFields: string[];
|
|
@@ -84,10 +143,13 @@ export type FunctionSpec = {
|
|
|
84
143
|
*/
|
|
85
144
|
// returnsBoolean?: boolean;
|
|
86
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Number of arguments expected
|
|
148
|
+
*/
|
|
87
149
|
numArgs: number;
|
|
88
150
|
|
|
89
151
|
/**
|
|
90
|
-
* If provided then the number of column names provided to the function must not be less than this
|
|
152
|
+
* If provided then the number of column names provided to the function (from getFields()) must not be less than this
|
|
91
153
|
* By default every function is checked against numArgs
|
|
92
154
|
*/
|
|
93
155
|
minCols?: number;
|
|
@@ -97,7 +159,7 @@ export type FunctionSpec = {
|
|
|
97
159
|
* getFields: string[] -> used to validate user supplied field names. It will be fired before querying to validate against allowed columns
|
|
98
160
|
* if not field names are used from arguments then return an empty array
|
|
99
161
|
*/
|
|
100
|
-
getFields: (args: any[]
|
|
162
|
+
getFields: (args: any[]) => "*" | string[];
|
|
101
163
|
/**
|
|
102
164
|
* allowedFields passed for multicol functions (e.g.: $rowhash)
|
|
103
165
|
*/
|
|
@@ -652,7 +714,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
652
714
|
numArgs: 1,
|
|
653
715
|
minCols: 0,
|
|
654
716
|
singleColArg: false,
|
|
655
|
-
getFields: (args: any[], allowedFields) =>
|
|
717
|
+
getFields: (args: any[], allowedFields) => [], // Fields not validated because we'll use the allowed ones anyway
|
|
656
718
|
getQuery: ({ allowedFields, args, tableAlias }) => {
|
|
657
719
|
let value = asValue(args[0]);
|
|
658
720
|
if(typeof value !== "string") throw "expecting string argument";
|
|
@@ -955,28 +1017,16 @@ export class SelectItemBuilder {
|
|
|
955
1017
|
this.select.push(item);
|
|
956
1018
|
}
|
|
957
1019
|
|
|
958
|
-
private
|
|
959
|
-
const funcDef =
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
throw "\n Function " + funcName + " does not exist or is not allowed " + hint;
|
|
964
|
-
}
|
|
965
|
-
this.addFunction(funcDef, args, alias);
|
|
966
|
-
}
|
|
1020
|
+
private addFunction = (func: FunctionSpec | string, args: any[], alias: string) => {
|
|
1021
|
+
const funcDef = parseFunction({
|
|
1022
|
+
func, args, functions: this.functions,
|
|
1023
|
+
allowedFields: this.allowedFieldsIncludingComputed,
|
|
1024
|
+
});
|
|
967
1025
|
|
|
968
|
-
private addFunction = (funcDef: FunctionSpec, args: any[], alias: string) => {
|
|
969
|
-
if(funcDef.numArgs) {
|
|
970
|
-
const fields = funcDef.getFields(args, this.allowedFields);
|
|
971
|
-
if(funcDef.minCols !== 0 && fields !== "*" && Array.isArray(fields) && !fields.length ){
|
|
972
|
-
console.log(fields)
|
|
973
|
-
throw `\n Function "${funcDef.name}" expects at least a field name but has not been provided with one`;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
1026
|
this.addItem({
|
|
977
1027
|
type: funcDef.type,
|
|
978
1028
|
alias,
|
|
979
|
-
getFields: () => funcDef.getFields(args
|
|
1029
|
+
getFields: () => funcDef.getFields(args),
|
|
980
1030
|
getQuery: (tableAlias?: string) => funcDef.getQuery({ allColumns: this.columns, allowedFields: this.allowedFields, args, tableAlias,
|
|
981
1031
|
ctidField: undefined,
|
|
982
1032
|
|
|
@@ -1092,14 +1142,13 @@ export class SelectItemBuilder {
|
|
|
1092
1142
|
}
|
|
1093
1143
|
funcName = val;
|
|
1094
1144
|
args = [key];
|
|
1145
|
+
|
|
1146
|
+
/** Function full notation { $funcName: ["colName", ...args] } */
|
|
1095
1147
|
} else {
|
|
1096
|
-
|
|
1097
|
-
if(callKeys.length !== 1 || !Array.isArray(val[callKeys[0]])) throw "\nIssue with select. \nUnexpected function definition. \nExpecting { field_name: func_name } OR { result_key: { func_name: [arg1, arg2 ...] } } \nBut got -> " + JSON.stringify({ [key]: val });
|
|
1098
|
-
funcName = callKeys[0];
|
|
1099
|
-
args = val[funcName];
|
|
1148
|
+
({ funcName, args } = parseFunctionObject(val));
|
|
1100
1149
|
}
|
|
1101
1150
|
|
|
1102
|
-
this.
|
|
1151
|
+
this.addFunction(funcName, args, key);
|
|
1103
1152
|
|
|
1104
1153
|
/* Join */
|
|
1105
1154
|
} else {
|
package/lib/utils.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prostgles-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.146",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,15 +24,13 @@
|
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://github.com/prostgles/prostgles-server-js#readme",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aws-sdk/client-s3": "^3.
|
|
28
|
-
"aws-sdk": "^2.
|
|
27
|
+
"@aws-sdk/client-s3": "^3.95.0",
|
|
28
|
+
"aws-sdk": "^2.1141.0",
|
|
29
29
|
"bluebird": "^3.7.2",
|
|
30
30
|
"file-type": "^16.5.3",
|
|
31
|
-
"
|
|
32
|
-
"npm": "^8.1.4",
|
|
33
|
-
"pg-promise": "^10.9.5",
|
|
31
|
+
"pg-promise": "^10.11.1",
|
|
34
32
|
"prostgles-types": "^1.5.123",
|
|
35
|
-
"sharp": "^0.30.
|
|
33
|
+
"sharp": "^0.30.5"
|
|
36
34
|
},
|
|
37
35
|
"devDependencies": {
|
|
38
36
|
"@aws-sdk/types": "^3.34.0",
|
package/tests/client/PID.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
15765
|
|
@@ -313,6 +313,27 @@ async function isomorphic(db) {
|
|
|
313
313
|
const res = await db.various.count({ "jsn->a->>b": '3' });
|
|
314
314
|
assert_1.strict.equal(res, 1);
|
|
315
315
|
});
|
|
316
|
+
await tryRun("Complex filtering", async () => {
|
|
317
|
+
const res = await db.various.count({
|
|
318
|
+
$and: [
|
|
319
|
+
{
|
|
320
|
+
$filter: [
|
|
321
|
+
{ $year: ["added"] },
|
|
322
|
+
"=",
|
|
323
|
+
'1996'
|
|
324
|
+
]
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
$filter: [
|
|
328
|
+
{ $Mon: ["added"] },
|
|
329
|
+
"=",
|
|
330
|
+
'Dec'
|
|
331
|
+
]
|
|
332
|
+
}
|
|
333
|
+
]
|
|
334
|
+
});
|
|
335
|
+
assert_1.strict.equal(res, 1);
|
|
336
|
+
});
|
|
316
337
|
await tryRun("template_string function", async () => {
|
|
317
338
|
const res = await db.various.findOne({ name: 'abc9' }, { select: { tstr: { $template_string: ["{name} is hehe"] } } });
|
|
318
339
|
const res2 = await db.various.findOne({ name: 'abc9' }, { select: { tstr: { $template_string: ["is hehe"] } } });
|
|
@@ -353,6 +353,29 @@ export default async function isomorphic(db: Partial<DbHandler> | Partial<DBHand
|
|
|
353
353
|
assert.equal(res, 1)
|
|
354
354
|
});
|
|
355
355
|
|
|
356
|
+
await tryRun("Complex filtering", async () => {
|
|
357
|
+
const res = await db.various.count({
|
|
358
|
+
$and: [
|
|
359
|
+
{
|
|
360
|
+
$filter: [
|
|
361
|
+
{ $year: ["added"] },
|
|
362
|
+
"=",
|
|
363
|
+
'1996'
|
|
364
|
+
]
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
$filter: [
|
|
368
|
+
{ $Mon: ["added"] },
|
|
369
|
+
"=",
|
|
370
|
+
'Dec'
|
|
371
|
+
]
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
]
|
|
375
|
+
});
|
|
376
|
+
assert.equal(res, 1)
|
|
377
|
+
});
|
|
378
|
+
|
|
356
379
|
await tryRun("template_string function", async () => {
|
|
357
380
|
const res = await db.various.findOne({ name: 'abc9' }, { select: { tstr: { $template_string: ["{name} is hehe"] } } });
|
|
358
381
|
const res2 = await db.various.findOne({ name: 'abc9' }, { select: { tstr: { $template_string: ["is hehe"] } } });
|
|
@@ -23,18 +23,16 @@
|
|
|
23
23
|
},
|
|
24
24
|
"../..": {
|
|
25
25
|
"name": "prostgles-server",
|
|
26
|
-
"version": "2.0.
|
|
26
|
+
"version": "2.0.145",
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@aws-sdk/client-s3": "^3.
|
|
30
|
-
"aws-sdk": "^2.
|
|
29
|
+
"@aws-sdk/client-s3": "^3.95.0",
|
|
30
|
+
"aws-sdk": "^2.1141.0",
|
|
31
31
|
"bluebird": "^3.7.2",
|
|
32
32
|
"file-type": "^16.5.3",
|
|
33
|
-
"
|
|
34
|
-
"npm": "^8.1.4",
|
|
35
|
-
"pg-promise": "^10.9.5",
|
|
33
|
+
"pg-promise": "^10.11.1",
|
|
36
34
|
"prostgles-types": "^1.5.123",
|
|
37
|
-
"sharp": "^0.30.
|
|
35
|
+
"sharp": "^0.30.5"
|
|
38
36
|
},
|
|
39
37
|
"devDependencies": {
|
|
40
38
|
"@aws-sdk/types": "^3.34.0",
|
|
@@ -1428,18 +1426,16 @@
|
|
|
1428
1426
|
"prostgles-server": {
|
|
1429
1427
|
"version": "file:../..",
|
|
1430
1428
|
"requires": {
|
|
1431
|
-
"@aws-sdk/client-s3": "^3.
|
|
1429
|
+
"@aws-sdk/client-s3": "^3.95.0",
|
|
1432
1430
|
"@aws-sdk/types": "^3.34.0",
|
|
1433
1431
|
"@types/bluebird": "^3.5.36",
|
|
1434
1432
|
"@types/node": "^14.14.35",
|
|
1435
|
-
"aws-sdk": "^2.
|
|
1433
|
+
"aws-sdk": "^2.1141.0",
|
|
1436
1434
|
"bluebird": "^3.7.2",
|
|
1437
1435
|
"file-type": "^16.5.3",
|
|
1438
|
-
"
|
|
1439
|
-
"npm": "^8.1.4",
|
|
1440
|
-
"pg-promise": "^10.9.5",
|
|
1436
|
+
"pg-promise": "^10.11.1",
|
|
1441
1437
|
"prostgles-types": "^1.5.123",
|
|
1442
|
-
"sharp": "^0.30.
|
|
1438
|
+
"sharp": "^0.30.5",
|
|
1443
1439
|
"typescript": "^3.9.7"
|
|
1444
1440
|
}
|
|
1445
1441
|
},
|