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.
@@ -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[], allowedFields: string[]) => "*" | string[];
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) => allowedFields.filter(fName => args?.[0]?.includes(`{${fName}}`)),
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 addFunctionByName = (funcName: string, args: any[], alias: string) => {
959
- const funcDef = this.functions.find(f => f.name === funcName);
960
- if(!funcDef) {
961
- const sf = this.functions.filter(f => f.name.toLowerCase().slice(1).startsWith(funcName.toLowerCase())).sort((a, b) => (a.name.length - b.name.length));
962
- const hint = (sf.length? `. \n Maybe you meant: \n | ${sf.map(s => s.name + " " + (s.description || "")).join(" \n | ")} ?` : "");
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, this.allowedFields),
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
- const callKeys = Object.keys(val);
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.addFunctionByName(funcName, args, key);
1151
+ this.addFunction(funcName, args, key);
1103
1152
 
1104
1153
  /* Join */
1105
1154
  } else {
package/lib/utils.ts CHANGED
@@ -1 +1,6 @@
1
- export { get } from "prostgles-types";
1
+ export { get } from "prostgles-types";
2
+
3
+
4
+ export function isObject(obj: any){
5
+ return Boolean(obj && typeof obj === "object" && !Array.isArray(obj) );
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "2.0.145",
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.86.0",
28
- "aws-sdk": "^2.1134.0",
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
- "i": "^0.3.7",
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.4"
33
+ "sharp": "^0.30.5"
36
34
  },
37
35
  "devDependencies": {
38
36
  "@aws-sdk/types": "^3.34.0",
@@ -1 +1 @@
1
- 7614
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.144",
26
+ "version": "2.0.145",
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@aws-sdk/client-s3": "^3.86.0",
30
- "aws-sdk": "^2.1134.0",
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
- "i": "^0.3.7",
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.4"
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.86.0",
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.1134.0",
1433
+ "aws-sdk": "^2.1141.0",
1436
1434
  "bluebird": "^3.7.2",
1437
1435
  "file-type": "^16.5.3",
1438
- "i": "^0.3.7",
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.4",
1438
+ "sharp": "^0.30.5",
1443
1439
  "typescript": "^3.9.7"
1444
1440
  }
1445
1441
  },