prostgles-server 2.0.278 → 2.0.281

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.
@@ -1,11 +1,11 @@
1
- import { getKeys, RULE_METHODS, AnyObject, get, TableSchemaForClient, DBSchemaTable, MethodKey, TableInfo, FullFilter } from "prostgles-types";
1
+ import { getKeys, RULE_METHODS, AnyObject, get, TableSchemaForClient, DBSchemaTable, MethodKey, TableInfo, FullFilter, isObject } from "prostgles-types";
2
2
  import { ClientInfo } from "./AuthHandler";
3
3
  import { CommonTableRules, Filter, isPlainObject, LocalParams, PRGLIOSocket, TableHandler, ViewHandler } from "./DboBuilder";
4
4
  import type { TableHandler as TableHandlerFromTypes } from "prostgles-types";
5
5
  import { Prostgles, DBHandlerServer, DB, TABLE_METHODS } from "./Prostgles";
6
6
  import type { DBOFullyTyped, PublishFullyTyped } from "./DBSchemaBuilder";
7
- export type Method = (...args: any) => ( any | Promise<any> );
8
- export type PublishMethods<S = void> = (params: PublishParams<S>) => { [key:string]: Method } | Promise<{ [key:string]: Method } | null>;
7
+ export type Method = (...args: any) => (any | Promise<any>);
8
+ export type PublishMethods<S = void> = (params: PublishParams<S>) => { [key: string]: Method } | Promise<{ [key: string]: Method } | null>;
9
9
 
10
10
  export type Awaitable<T> = T | Promise<T>;
11
11
 
@@ -24,77 +24,77 @@ type DboTableCommand = Request & DboTable & {
24
24
  }
25
25
 
26
26
  const RULE_TO_METHODS = [
27
- {
28
- rule: "getColumns",
29
- sqlRule: "select",
30
- methods: RULE_METHODS.getColumns,
31
- no_limits: true,
32
- allowed_params: [],
33
- table_only: false,
34
- hint: ` expecting false | true | undefined`
27
+ {
28
+ rule: "getColumns",
29
+ sqlRule: "select",
30
+ methods: RULE_METHODS.getColumns,
31
+ no_limits: true,
32
+ allowed_params: [],
33
+ table_only: false,
34
+ hint: ` expecting false | true | undefined`
35
35
  },
36
- {
37
- rule: "getInfo",
38
- sqlRule: "select",
39
- methods: RULE_METHODS.getInfo,
40
- no_limits: true,
41
- allowed_params: [],
42
- table_only: false,
43
- hint: ` expecting false | true | undefined`
36
+ {
37
+ rule: "getInfo",
38
+ sqlRule: "select",
39
+ methods: RULE_METHODS.getInfo,
40
+ no_limits: true,
41
+ allowed_params: [],
42
+ table_only: false,
43
+ hint: ` expecting false | true | undefined`
44
44
  },
45
- {
46
- rule: "insert",
47
- sqlRule: "insert",
48
- methods: RULE_METHODS.insert,
49
- no_limits: <SelectRule>{ fields: "*" },
50
- table_only: true,
51
- allowed_params: <Array<keyof InsertRule>>["fields", "forcedData", "returningFields", "validate", "preValidate", "postValidate"] ,
52
- hint: ` expecting "*" | true | { fields: string | string[] | {} }`
45
+ {
46
+ rule: "insert",
47
+ sqlRule: "insert",
48
+ methods: RULE_METHODS.insert,
49
+ no_limits: <SelectRule>{ fields: "*" },
50
+ table_only: true,
51
+ allowed_params: <Array<keyof InsertRule>>["fields", "forcedData", "returningFields", "validate", "preValidate", "postValidate"],
52
+ hint: ` expecting "*" | true | { fields: string | string[] | {} }`
53
53
  },
54
- {
55
- rule: "update",
56
- sqlRule: "update",
57
- methods: RULE_METHODS.update,
58
- no_limits: <UpdateRule>{ fields: "*", filterFields: "*", returningFields: "*" },
59
- table_only: true,
60
- allowed_params: <Array<keyof UpdateRule>>["fields", "filterFields", "forcedFilter", "forcedData", "returningFields", "validate", "dynamicFields"] ,
61
- hint: ` expecting "*" | true | { fields: string | string[] | {} }`
54
+ {
55
+ rule: "update",
56
+ sqlRule: "update",
57
+ methods: RULE_METHODS.update,
58
+ no_limits: <UpdateRule>{ fields: "*", filterFields: "*", returningFields: "*" },
59
+ table_only: true,
60
+ allowed_params: <Array<keyof UpdateRule>>["fields", "filterFields", "forcedFilter", "forcedData", "returningFields", "validate", "dynamicFields"],
61
+ hint: ` expecting "*" | true | { fields: string | string[] | {} }`
62
62
  },
63
- {
64
- rule: "select",
65
- sqlRule: "select",
66
- methods: RULE_METHODS.select,
67
- no_limits: <SelectRule>{ fields: "*", filterFields: "*" },
68
- table_only: false,
69
- allowed_params: <Array<keyof SelectRule>>["fields", "filterFields", "forcedFilter", "validate", "maxLimit"] ,
70
- hint: ` expecting "*" | true | { fields: ( string | string[] | {} ) }`
63
+ {
64
+ rule: "select",
65
+ sqlRule: "select",
66
+ methods: RULE_METHODS.select,
67
+ no_limits: <SelectRule>{ fields: "*", filterFields: "*" },
68
+ table_only: false,
69
+ allowed_params: <Array<keyof SelectRule>>["fields", "filterFields", "forcedFilter", "validate", "maxLimit"],
70
+ hint: ` expecting "*" | true | { fields: ( string | string[] | {} ) }`
71
71
  },
72
- {
73
- rule: "delete",
74
- sqlRule: "delete",
75
- methods: RULE_METHODS.delete,
76
- no_limits: <DeleteRule>{ filterFields: "*" } ,
77
- table_only: true,
78
- allowed_params: <Array<keyof DeleteRule>>["filterFields", "forcedFilter", "returningFields", "validate"],
79
- hint: ` expecting "*" | true | { filterFields: ( string | string[] | {} ) } \n Will use "select", "update", "delete" and "insert" rules`
72
+ {
73
+ rule: "delete",
74
+ sqlRule: "delete",
75
+ methods: RULE_METHODS.delete,
76
+ no_limits: <DeleteRule>{ filterFields: "*" },
77
+ table_only: true,
78
+ allowed_params: <Array<keyof DeleteRule>>["filterFields", "forcedFilter", "returningFields", "validate"],
79
+ hint: ` expecting "*" | true | { filterFields: ( string | string[] | {} ) } \n Will use "select", "update", "delete" and "insert" rules`
80
80
  },
81
- {
82
- rule: "sync",
83
- sqlRule: "select",
84
- methods: RULE_METHODS.sync,
85
- no_limits: null,
86
- table_only: true,
87
- allowed_params: <Array<keyof SyncRule>>["id_fields", "synced_field", "sync_type", "allow_delete", "throttle", "batch_size"],
88
- hint: ` expecting "*" | true | { id_fields: string[], synced_field: string }`
81
+ {
82
+ rule: "sync",
83
+ sqlRule: "select",
84
+ methods: RULE_METHODS.sync,
85
+ no_limits: null,
86
+ table_only: true,
87
+ allowed_params: <Array<keyof SyncRule>>["id_fields", "synced_field", "sync_type", "allow_delete", "throttle", "batch_size"],
88
+ hint: ` expecting "*" | true | { id_fields: string[], synced_field: string }`
89
89
  },
90
- {
91
- rule: "subscribe",
92
- sqlRule: "select",
93
- methods: RULE_METHODS.subscribe,
94
- no_limits: <SubscribeRule>{ throttle: 0 },
95
- table_only: true,
96
- allowed_params: <Array<keyof SubscribeRule>>["throttle"],
97
- hint: ` expecting "*" | true | { throttle: number } \n Will use "select" rules`
90
+ {
91
+ rule: "subscribe",
92
+ sqlRule: "select",
93
+ methods: RULE_METHODS.subscribe,
94
+ no_limits: <SubscribeRule>{ throttle: 0 },
95
+ table_only: true,
96
+ allowed_params: <Array<keyof SubscribeRule>>["throttle"],
97
+ hint: ` expecting "*" | true | { throttle: number } \n Will use "select" rules`
98
98
  }
99
99
  ] as const;
100
100
 
@@ -102,16 +102,16 @@ import { FieldFilter, SelectParams } from "prostgles-types";
102
102
  import { DEFAULT_SYNC_BATCH_SIZE } from "./PubSubManager";
103
103
 
104
104
  export type InsertRequestData = {
105
- data: object | object[]
106
- returning: FieldFilter;
105
+ data: object | object[]
106
+ returning: FieldFilter;
107
107
  }
108
108
  export type SelectRequestData = {
109
- filter: object;
110
- params: SelectParams;
109
+ filter: object;
110
+ params: SelectParams;
111
111
  }
112
112
  export type DeleteRequestData = {
113
- filter: object;
114
- returning: FieldFilter;
113
+ filter: object;
114
+ returning: FieldFilter;
115
115
  }
116
116
  export type UpdateRequestDataOne<R> = {
117
117
  filter: FullFilter<R>
@@ -123,7 +123,7 @@ export type UpdateReq<R> = {
123
123
  data: Partial<R>;
124
124
  }
125
125
  export type UpdateRequestDataBatch<R> = {
126
- data: UpdateReq<R>[];
126
+ data: UpdateReq<R>[];
127
127
  }
128
128
  export type UpdateRequestData<R extends AnyObject = AnyObject> = UpdateRequestDataOne<R> | UpdateRequestDataBatch<R>;
129
129
 
@@ -133,165 +133,165 @@ export type ValidateUpdateRow<R extends AnyObject = AnyObject, S = void> = (args
133
133
 
134
134
  export type SelectRule<Cols extends AnyObject = AnyObject, S = void> = {
135
135
 
136
- /**
137
- * Fields allowed to be selected. Tip: Use false to exclude field
138
- */
139
- fields: FieldFilter<Cols>;
136
+ /**
137
+ * Fields allowed to be selected. Tip: Use false to exclude field
138
+ */
139
+ fields: FieldFilter<Cols>;
140
140
 
141
- /**
142
- * The maximum number of rows a user can get in a select query. null by default. Unless a null or higher limit is specified 100 rows will be returned by the default
143
- */
144
- maxLimit?: number | null;
141
+ /**
142
+ * The maximum number of rows a user can get in a select query. null by default. Unless a null or higher limit is specified 100 rows will be returned by the default
143
+ */
144
+ maxLimit?: number | null;
145
145
 
146
- /**
147
- * Filter added to every query (e.g. user_id) to restrict access
148
- */
149
- forcedFilter?: FullFilter<Cols, S>;
146
+ /**
147
+ * Filter added to every query (e.g. user_id) to restrict access
148
+ */
149
+ forcedFilter?: FullFilter<Cols, S>;
150
150
 
151
- /**
152
- * Fields user can filter by
153
- * */
154
- filterFields?: FieldFilter<Cols>;
151
+ /**
152
+ * Fields user can filter by
153
+ * */
154
+ filterFields?: FieldFilter<Cols>;
155
155
 
156
- /**
157
- * Validation logic to check/update data for each request
158
- */
159
- validate?(args: SelectRequestData): SelectRequestData | Promise<SelectRequestData>;
156
+ /**
157
+ * Validation logic to check/update data for each request
158
+ */
159
+ validate?(args: SelectRequestData): SelectRequestData | Promise<SelectRequestData>;
160
160
 
161
161
  }
162
162
  export type InsertRule<Cols extends AnyObject = AnyObject, S = void> = {
163
163
 
164
- /**
165
- * Fields allowed to be inserted. Tip: Use false to exclude field
166
- */
167
- fields: SelectRule<Cols>["fields"]
164
+ /**
165
+ * Fields allowed to be inserted. Tip: Use false to exclude field
166
+ */
167
+ fields: SelectRule<Cols>["fields"]
168
+
169
+ /**
170
+ * Data to include/overwrite on each insert
171
+ */
172
+ forcedData?: Partial<Cols>;
168
173
 
169
- /**
170
- * Data to include/overwrite on each insert
171
- */
172
- forcedData?: Partial<Cols>;
173
-
174
- /**
175
- * Fields user can view after inserting
176
- */
177
- returningFields?: SelectRule<Cols>["fields"]
178
-
179
- /**
180
- * Validation logic to check/update data for each request. Happens before publish rule checks (for fields, forcedData/forcedFilter)
181
- */
182
- preValidate?: ValidateRow<Cols, S>;
183
-
184
- /**
185
- * Validation logic to check/update data for each request. Happens after publish rule checks (for fields, forcedData/forcedFilter)
186
- */
187
- validate?: ValidateRow<Cols, S>;
188
-
189
- /**
190
- * Validation logic to check/update data after the insert.
191
- * Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
192
- */
193
- postValidate?: ValidateRow<Required<Cols>, S>;
174
+ /**
175
+ * Fields user can view after inserting
176
+ */
177
+ returningFields?: SelectRule<Cols>["fields"]
178
+
179
+ /**
180
+ * Validation logic to check/update data for each request. Happens before publish rule checks (for fields, forcedData/forcedFilter)
181
+ */
182
+ preValidate?: ValidateRow<Cols, S>;
183
+
184
+ /**
185
+ * Validation logic to check/update data for each request. Happens after publish rule checks (for fields, forcedData/forcedFilter)
186
+ */
187
+ validate?: ValidateRow<Cols, S>;
188
+
189
+ /**
190
+ * Validation logic to check/update data after the insert.
191
+ * Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
192
+ */
193
+ postValidate?: ValidateRow<Required<Cols>, S>;
194
194
  }
195
195
  export type UpdateRule<Cols extends AnyObject = AnyObject, S = void> = {
196
196
 
197
- /**
198
- * Fields allowed to be updated. Tip: Use false/0 to exclude field
199
- */
197
+ /**
198
+ * Fields allowed to be updated. Tip: Use false/0 to exclude field
199
+ */
200
+ fields: SelectRule<Cols>["fields"]
201
+
202
+ /**
203
+ * Row level FGAC
204
+ * Used when the editable fields change based on the updated row
205
+ * If specified then the fields from the first matching filter table.count({ ...filter, ...updateFilter }) > 0 will be used
206
+ * If none matching then the "fields" will be used
207
+ * Specify in decreasing order of specificity otherwise a more general filter will match first
208
+ */
209
+ dynamicFields?: {
210
+ filter: SelectRule<Cols, S>["forcedFilter"]
200
211
  fields: SelectRule<Cols>["fields"]
201
-
202
- /**
203
- * Row level FGAC
204
- * Used when the editable fields change based on the updated row
205
- * If specified then the fields from the first matching filter table.count({ ...filter, ...updateFilter }) > 0 will be used
206
- * If none matching then the "fields" will be used
207
- * Specify in decreasing order of specificity otherwise a more general filter will match first
208
- */
209
- dynamicFields?: {
210
- filter: SelectRule<Cols, S>["forcedFilter"]
211
- fields: SelectRule<Cols>["fields"]
212
- }[];
213
-
214
- /**
215
- * Filter added to every query (e.g. user_id) to restrict access
216
- * This filter cannot be updated
217
- */
218
- forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
219
-
220
- /**
221
- * Data to include/overwrite on each updatDBe
222
- */
223
- forcedData?: InsertRule<Cols, S>["forcedData"]
224
-
225
- /**
226
- * Fields user can use to find the updates
227
- */
228
- filterFields?: SelectRule<Cols>["fields"]
229
-
230
- /**
231
- * Fields user can view after updating
232
- */
233
- returningFields?: SelectRule<Cols>["fields"]
234
-
235
- /**
236
- * Validation logic to check/update data for each request
237
- */
238
- validate?: ValidateUpdateRow<Cols, S>;
239
-
240
- /**
241
- * Validation logic to check/update data after the insert.
242
- * Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
243
- */
244
- postValidate?: ValidateRow<Required<Cols>, S>;
212
+ }[];
213
+
214
+ /**
215
+ * Filter added to every query (e.g. user_id) to restrict access
216
+ * This filter cannot be updated
217
+ */
218
+ forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
219
+
220
+ /**
221
+ * Data to include/overwrite on each updatDBe
222
+ */
223
+ forcedData?: InsertRule<Cols, S>["forcedData"]
224
+
225
+ /**
226
+ * Fields user can use to find the updates
227
+ */
228
+ filterFields?: SelectRule<Cols>["fields"]
229
+
230
+ /**
231
+ * Fields user can view after updating
232
+ */
233
+ returningFields?: SelectRule<Cols>["fields"]
234
+
235
+ /**
236
+ * Validation logic to check/update data for each request
237
+ */
238
+ validate?: ValidateUpdateRow<Cols, S>;
239
+
240
+ /**
241
+ * Validation logic to check/update data after the insert.
242
+ * Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
243
+ */
244
+ postValidate?: ValidateRow<Required<Cols>, S>;
245
245
  };
246
246
 
247
247
  export type DeleteRule<Cols extends AnyObject = AnyObject, S = void> = {
248
-
249
- /**
250
- * Filter added to every query (e.g. user_id) to restrict access
251
- */
252
- forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
253
-
254
- /**
255
- * Fields user can filter by
256
- */
257
- filterFields: FieldFilter<Cols>;
258
-
259
- /**
260
- * Fields user can view after deleting
261
- */
262
- returningFields?: SelectRule<Cols>["filterFields"]
263
-
264
- /**
265
- * Validation logic to check/update data for each request
266
- */
267
- validate?(...args: any[]): Awaitable<void>;// UpdateRequestData<Cols>;
248
+
249
+ /**
250
+ * Filter added to every query (e.g. user_id) to restrict access
251
+ */
252
+ forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
253
+
254
+ /**
255
+ * Fields user can filter by
256
+ */
257
+ filterFields: FieldFilter<Cols>;
258
+
259
+ /**
260
+ * Fields user can view after deleting
261
+ */
262
+ returningFields?: SelectRule<Cols>["filterFields"]
263
+
264
+ /**
265
+ * Validation logic to check/update data for each request
266
+ */
267
+ validate?(...args: any[]): Awaitable<void>;// UpdateRequestData<Cols>;
268
268
  }
269
269
  export type SyncRule<Cols extends AnyObject = AnyObject> = {
270
-
271
- /**
272
- * Primary keys used in updating data
273
- */
274
- id_fields: (keyof Cols)[];
275
-
276
- /**
277
- * Numerical incrementing fieldname (last updated timestamp) used to sync items
278
- */
279
- synced_field: keyof Cols;
280
-
281
- /**
282
- * EXPERIMENTAL. Disabled by default. If true then server will attempt to delete any records missing from client.
283
- */
284
- allow_delete?: boolean;
285
-
286
- /**
287
- * Throttle replication transmission in milliseconds. Defaults to 100
288
- */
289
- throttle?: number;
290
-
291
- /**
292
- * Number of rows to send per trip. Defaults to 50
293
- */
294
- batch_size?: number;
270
+
271
+ /**
272
+ * Primary keys used in updating data
273
+ */
274
+ id_fields: (keyof Cols)[];
275
+
276
+ /**
277
+ * Numerical incrementing fieldname (last updated timestamp) used to sync items
278
+ */
279
+ synced_field: keyof Cols;
280
+
281
+ /**
282
+ * EXPERIMENTAL. Disabled by default. If true then server will attempt to delete any records missing from client.
283
+ */
284
+ allow_delete?: boolean;
285
+
286
+ /**
287
+ * Throttle replication transmission in milliseconds. Defaults to 100
288
+ */
289
+ throttle?: number;
290
+
291
+ /**
292
+ * Number of rows to send per trip. Defaults to 50
293
+ */
294
+ batch_size?: number;
295
295
  }
296
296
  export type SubscribeRule = {
297
297
  throttle?: number;
@@ -349,13 +349,13 @@ export type PublishParams<S = void> = {
349
349
  }
350
350
  export type RequestParams = { dbo?: DBHandlerServer, socket?: any };
351
351
  export type PublishAllOrNothing = true | "*" | false | null;
352
- type PublishObject = {
353
- [table_name: string]: (PublishTableRule | PublishViewRule | PublishAllOrNothing )
352
+ type PublishObject = {
353
+ [table_name: string]: (PublishTableRule | PublishViewRule | PublishAllOrNothing)
354
354
  };
355
- export type ParsedPublishTables = {
355
+ export type ParsedPublishTables = {
356
356
  [table_name: string]: ParsedPublishTable
357
357
  };
358
- export type PublishedResult<Schema = void> = PublishAllOrNothing | PublishFullyTyped<Schema> ;
358
+ export type PublishedResult<Schema = void> = PublishAllOrNothing | PublishFullyTyped<Schema>;
359
359
  export type Publish<Schema = void> = PublishedResult<Schema> | ((params: PublishParams<Schema>) => Awaitable<PublishedResult<Schema>>);
360
360
 
361
361
  export class PublishParser {
@@ -366,45 +366,45 @@ export class PublishParser {
366
366
  db: DB
367
367
  prostgles: Prostgles;
368
368
 
369
- constructor(publish: any, publishMethods: any, publishRawSQL: any, dbo: DBHandlerServer, db: DB, prostgles: Prostgles){
370
- this.publish = publish;
371
- this.publishMethods = publishMethods;
372
- this.publishRawSQL = publishRawSQL;
373
- this.dbo = dbo;
374
- this.db = db;
375
- this.prostgles = prostgles;
369
+ constructor(publish: any, publishMethods: any, publishRawSQL: any, dbo: DBHandlerServer, db: DB, prostgles: Prostgles) {
370
+ this.publish = publish;
371
+ this.publishMethods = publishMethods;
372
+ this.publishRawSQL = publishRawSQL;
373
+ this.dbo = dbo;
374
+ this.db = db;
375
+ this.prostgles = prostgles;
376
376
 
377
- if(!this.dbo || !this.publish) throw "INTERNAL ERROR: dbo and/or publish missing";
377
+ if (!this.dbo || !this.publish) throw "INTERNAL ERROR: dbo and/or publish missing";
378
378
  }
379
379
 
380
380
  async getPublishParams(localParams: LocalParams, clientInfo?: ClientInfo): Promise<PublishParams> {
381
- if(!this.dbo) throw "dbo missing"
382
- return {
383
- ...(clientInfo || await this.prostgles.authHandler?.getClientInfo(localParams)),
384
- dbo: this.dbo,
385
- db: this.db,
386
- socket: localParams.socket!
387
- }
381
+ if (!this.dbo) throw "dbo missing"
382
+ return {
383
+ ...(clientInfo || await this.prostgles.authHandler?.getClientInfo(localParams)),
384
+ dbo: this.dbo,
385
+ db: this.db,
386
+ socket: localParams.socket!
387
+ }
388
388
  }
389
389
 
390
- async getMethods(socket: any){
391
- let methods = {};
392
-
393
- const publishParams = await this.getPublishParams({ socket });
394
- const _methods = await applyParamsIfFunc(this.publishMethods, publishParams);
395
-
396
- if(_methods && Object.keys(_methods).length){
397
- getKeys(_methods).map(key => {
398
- if(_methods[key] && (typeof _methods[key] === "function" || typeof _methods[key].then === "function")){
399
- //@ts-ignore
400
- methods[key] = _methods[key];
401
- } else {
402
- throw `invalid publishMethods item -> ${key} \n Expecting a function or promise`
403
- }
404
- });
405
- }
406
-
407
- return methods;
390
+ async getMethods(socket: any) {
391
+ let methods = {};
392
+
393
+ const publishParams = await this.getPublishParams({ socket });
394
+ const _methods = await applyParamsIfFunc(this.publishMethods, publishParams);
395
+
396
+ if (_methods && Object.keys(_methods).length) {
397
+ getKeys(_methods).map(key => {
398
+ if (_methods[key] && (typeof _methods[key] === "function" || typeof _methods[key].then === "function")) {
399
+ //@ts-ignore
400
+ methods[key] = _methods[key];
401
+ } else {
402
+ throw `invalid publishMethods item -> ${key} \n Expecting a function or promise`
403
+ }
404
+ });
405
+ }
406
+
407
+ return methods;
408
408
  }
409
409
 
410
410
  /**
@@ -413,36 +413,36 @@ export class PublishParser {
413
413
  * @param user
414
414
  */
415
415
  async getPublish(localParams: LocalParams, clientInfo?: ClientInfo): Promise<PublishObject> {
416
- const publishParams = await this.getPublishParams(localParams, clientInfo)
417
- let _publish = await applyParamsIfFunc(this.publish, publishParams );
416
+ const publishParams = await this.getPublishParams(localParams, clientInfo)
417
+ let _publish = await applyParamsIfFunc(this.publish, publishParams);
418
418
 
419
- if(_publish === "*"){
420
- let publish = {} as any;
421
- this.prostgles.dboBuilder.tablesOrViews?.map(tov => {
422
- publish[tov.name] = "*";
423
- });
424
- return publish;
425
- }
419
+ if (_publish === "*") {
420
+ let publish = {} as any;
421
+ this.prostgles.dboBuilder.tablesOrViews?.map(tov => {
422
+ publish[tov.name] = "*";
423
+ });
424
+ return publish;
425
+ }
426
426
 
427
- return _publish;
427
+ return _publish;
428
428
  }
429
- async getValidatedRequestRuleWusr({ tableName, command, localParams }: DboTableCommand): Promise<TableRule>{
430
- const clientInfo = await this.prostgles.authHandler!.getClientInfo(localParams);
431
- return await this.getValidatedRequestRule({ tableName, command, localParams }, clientInfo);
429
+ async getValidatedRequestRuleWusr({ tableName, command, localParams }: DboTableCommand): Promise<TableRule> {
430
+ const clientInfo = await this.prostgles.authHandler!.getClientInfo(localParams);
431
+ return await this.getValidatedRequestRule({ tableName, command, localParams }, clientInfo);
432
432
  }
433
-
434
- async getValidatedRequestRule({ tableName, command, localParams }: DboTableCommand, clientInfo?: ClientInfo): Promise<TableRule>{
435
- if(!this.dbo) throw "INTERNAL ERROR: dbo is missing";
436
433
 
437
- if(!command || !tableName) throw "command OR tableName are missing";
434
+ async getValidatedRequestRule({ tableName, command, localParams }: DboTableCommand, clientInfo?: ClientInfo): Promise<TableRule> {
435
+ if (!this.dbo) throw "INTERNAL ERROR: dbo is missing";
436
+
437
+ if (!command || !tableName) throw "command OR tableName are missing";
438
438
 
439
439
  let rtm = RULE_TO_METHODS.find(rtms => (rtms.methods as any).includes(command));
440
- if(!rtm){
440
+ if (!rtm) {
441
441
  throw "Invalid command: " + command;
442
442
  }
443
443
 
444
444
  /* Must be local request -> allow everything */
445
- if(!localParams || (!localParams.socket && !localParams.httpReq)){
445
+ if (!localParams || (!localParams.socket && !localParams.httpReq)) {
446
446
  return RULE_TO_METHODS.reduce((a, v) => ({
447
447
  ...a,
448
448
  [v.rule]: v.no_limits
@@ -450,45 +450,47 @@ export class PublishParser {
450
450
  }
451
451
 
452
452
  /* Must be from socket. Must have a publish */
453
- if(!this.publish) throw "publish is missing";
453
+ if (!this.publish) throw "publish is missing";
454
454
 
455
455
  /* Get any publish errors for socket */
456
456
  const schm = localParams?.socket?.prostgles?.schema?.[tableName]?.[command];
457
457
 
458
- if(schm && schm.err) throw schm.err;
458
+ if (schm && schm.err) throw schm.err;
459
459
 
460
460
  let table_rule = await this.getTableRules({ tableName, localParams }, clientInfo);
461
- if(!table_rule) throw { stack: ["getValidatedRequestRule()"], message: "Invalid or disallowed table: " + tableName};
461
+ if (!table_rule) throw { stack: ["getValidatedRequestRule()"], message: "Invalid or disallowed table: " + tableName };
462
462
 
463
463
 
464
- if(command === "upsert"){
465
- if(!table_rule.update || !table_rule.insert){
464
+ if (command === "upsert") {
465
+ if (!table_rule.update || !table_rule.insert) {
466
466
  throw { stack: ["getValidatedRequestRule()"], message: `Invalid or disallowed command: upsert` };
467
467
  }
468
468
  }
469
469
 
470
- if(rtm && table_rule && table_rule[rtm.rule]){
470
+ if (rtm && table_rule && table_rule[rtm.rule]) {
471
471
  return table_rule;
472
472
  } else throw { stack: ["getValidatedRequestRule()"], message: `Invalid or disallowed command: ${tableName}.${command}` };
473
473
  }
474
-
474
+
475
475
  async getTableRules({ tableName, localParams }: DboTable, clientInfo?: ClientInfo): Promise<ParsedPublishTable | undefined> {
476
-
476
+
477
477
  try {
478
- if(!localParams || !tableName) throw { stack: ["getTableRules()"], message: "publish OR socket OR dbo OR tableName are missing" };
478
+ if (!localParams || !tableName) throw { stack: ["getTableRules()"], message: "publish OR socket OR dbo OR tableName are missing" };
479
479
 
480
480
  let _publish = await this.getPublish(localParams, clientInfo);
481
481
 
482
- const raw_table_rules = _publish[tableName];// applyParamsIfFunc(_publish[tableName], localParams, this.dbo, this.db, user);
483
- if(!raw_table_rules) return undefined;
482
+ const raw_table_rules = _publish[tableName];
483
+ if (!raw_table_rules || isObject(raw_table_rules) && Object.values(raw_table_rules).every(v => !v)) {
484
+ return undefined;
485
+ }
484
486
 
485
487
  let parsed_table: ParsedPublishTable = {};
486
488
 
487
489
  /* Get view or table specific rules */
488
490
  const tHandler = (this.dbo[tableName] as TableHandler | ViewHandler);
489
491
 
490
- if(!tHandler) throw { stack: ["getTableRules()"], message: `${tableName} could not be found in dbo` };
491
-
492
+ if (!tHandler) throw { stack: ["getTableRules()"], message: `${tableName} could not be found in dbo` };
493
+
492
494
  const is_view = tHandler.is_view;
493
495
  const MY_RULES = RULE_TO_METHODS.filter(r => {
494
496
 
@@ -496,13 +498,13 @@ export class PublishParser {
496
498
  const pgUserIsAllowedThis = tHandler.tableOrViewInfo.privileges[r.sqlRule];
497
499
  let result = (!is_view || !r.table_only) && pgUserIsAllowedThis;
498
500
 
499
- if(!pgUserIsAllowedThis && isPlainObject(raw_table_rules) && (raw_table_rules as PublishTableRule)[r.sqlRule]){
501
+ if (!pgUserIsAllowedThis && isPlainObject(raw_table_rules) && (raw_table_rules as PublishTableRule)[r.sqlRule]) {
500
502
  throw `Your postgres user is not allowed ${r.sqlRule} on table ${tableName}`;
501
503
  }
502
504
 
503
- if((r.rule === "subscribe" || r.rule === "sync") && !this.prostgles.isSuperUser){
505
+ if ((r.rule === "subscribe" || r.rule === "sync") && !this.prostgles.isSuperUser) {
504
506
  result = false;
505
- if(isPlainObject(raw_table_rules) && (raw_table_rules as PublishTableRule)[r.rule]){
507
+ if (isPlainObject(raw_table_rules) && (raw_table_rules as PublishTableRule)[r.rule]) {
506
508
  throw `Cannot publish realtime rule ${tableName}.${r.rule}. Superuser is required for this`
507
509
  }
508
510
  }
@@ -510,43 +512,43 @@ export class PublishParser {
510
512
  return result;
511
513
  });
512
514
 
513
-
515
+
514
516
 
515
517
  /* All methods allowed. Add no limits for table rules */
516
- if([true, "*"].includes(raw_table_rules as any)){
518
+ if ([true, "*"].includes(raw_table_rules as any)) {
517
519
  parsed_table = {};
518
520
  MY_RULES.filter(r => r.no_limits).forEach(r => {
519
521
  parsed_table[r.rule] = { ...r.no_limits as object } as any;
520
522
  });
521
523
 
522
- /** Specific rules allowed */
523
- } else if(isPlainObject(raw_table_rules) && getKeys(raw_table_rules).length){
524
+ /** Specific rules allowed */
525
+ } else if (isPlainObject(raw_table_rules) && getKeys(raw_table_rules).length) {
524
526
  const allRuleKeys: (keyof PublishViewRule | keyof PublishTableRule)[] = getKeys(raw_table_rules);
525
527
  const dissallowedRuleKeys = allRuleKeys.filter(m => !(raw_table_rules as PublishTableRule)[m])
526
528
 
527
529
  MY_RULES.map(r => {
528
530
  /** Unless specifically disabled these are allowed */
529
- if(["getInfo", "getColumns"].includes(r.rule) && !dissallowedRuleKeys.includes(r.rule as any)){
531
+ if (["getInfo", "getColumns"].includes(r.rule) && !dissallowedRuleKeys.includes(r.rule as any)) {
530
532
  parsed_table[r.rule] = r.no_limits as any;
531
- return ;
532
- }
533
-
533
+ return;
534
+ }
535
+
534
536
  /** Add no_limit values for implied/ fully allowed methods */
535
537
  if ([true, "*"].includes((raw_table_rules as PublishTableRule)[r.rule] as any) && r.no_limits) {
536
538
  parsed_table[r.rule] = Object.assign({}, r.no_limits) as any;
537
-
538
- /** Carry over detailed config */
539
- } else if(isPlainObject((raw_table_rules as any)[r.rule])){
539
+
540
+ /** Carry over detailed config */
541
+ } else if (isPlainObject((raw_table_rules as any)[r.rule])) {
540
542
  parsed_table[r.rule] = (raw_table_rules as any)[r.rule]
541
543
  }
542
544
  });
543
-
545
+
544
546
  allRuleKeys.filter(m => parsed_table[m])
545
547
  .find((method) => {
546
548
  let rm = MY_RULES.find(r => r.rule === method || (r.methods as readonly string[]).includes(method));
547
- if(!rm){
549
+ if (!rm) {
548
550
  let extraInfo = "";
549
- if(is_view && RULE_TO_METHODS.find(r => !is_view && r.rule === method || (r.methods as any).includes(method))){
551
+ if (is_view && RULE_TO_METHODS.find(r => !is_view && r.rule === method || (r.methods as any).includes(method))) {
550
552
  extraInfo = "You've specified table rules to a view\n";
551
553
  }
552
554
  throw `Invalid rule in publish.${tableName} -> ${method} \n${extraInfo}Expecting any of: ${MY_RULES.flatMap(r => [r.rule, ...r.methods]).join(", ")}`;
@@ -554,40 +556,40 @@ export class PublishParser {
554
556
 
555
557
  /* Check RULES for invalid params */
556
558
  /* Methods do not have params -> They use them from rules */
557
- if(method === rm.rule){
559
+ if (method === rm.rule) {
558
560
  let method_params = getKeys(parsed_table[method]);
559
561
  let iparam = method_params.find(p => !rm?.allowed_params.includes(<never>p));
560
- if(iparam){
562
+ if (iparam) {
561
563
  throw `Invalid setting in publish.${tableName}.${method} -> ${iparam}. \n Expecting any of: ${rm.allowed_params.join(", ")}`;
562
564
  }
563
565
  }
564
566
 
565
567
  /* Add default params (if missing) */
566
- if(method === "sync"){
567
-
568
- if([true, "*"].includes(parsed_table[method] as any)){
568
+ if (method === "sync") {
569
+
570
+ if ([true, "*"].includes(parsed_table[method] as any)) {
569
571
  throw "Invalid sync rule. Expecting { id_fields: string[], synced_field: string } ";
570
572
  }
571
-
572
- if(typeof parsed_table[method]?.throttle !== "number"){
573
+
574
+ if (typeof parsed_table[method]?.throttle !== "number") {
573
575
  parsed_table[method]!.throttle = 100;
574
576
  }
575
- if(typeof parsed_table[method]?.batch_size !== "number"){
577
+ if (typeof parsed_table[method]?.batch_size !== "number") {
576
578
  parsed_table[method]!.batch_size = DEFAULT_SYNC_BATCH_SIZE;
577
579
  }
578
580
  }
579
581
 
580
582
  /* Enable subscribe if not explicitly disabled */
581
583
  const subKey = "subscribe" as const;
582
-
583
- if(method === "select" && !dissallowedRuleKeys.includes(subKey)){
584
+
585
+ if (method === "select" && !dissallowedRuleKeys.includes(subKey)) {
584
586
  const sr = MY_RULES.find(r => r.rule === subKey);
585
- if(sr){
587
+ if (sr) {
586
588
  parsed_table[subKey] = { ...sr.no_limits as SubscribeRule };
587
589
  parsed_table.subscribeOne = { ...sr.no_limits as SubscribeRule };
588
590
  }
589
591
  }
590
- });
592
+ });
591
593
 
592
594
  } else {
593
595
  throw "Unexpected publish"
@@ -602,12 +604,12 @@ export class PublishParser {
602
604
  /** THIS IS A MESS -> some methods cannot be dissallowed (unsync, unsubscribe...) */
603
605
  r.methods.forEach(method => {
604
606
  const isAllowed = tableRules[r.rule] && (tableRules as any)[method] === undefined;
605
- if(isAllowed){
606
-
607
- if(method === "updateBatch" && !tableRules.update){
608
-
609
- } else if(method === "upsert" && (!tableRules.update || !tableRules.insert)){
610
-
607
+ if (isAllowed) {
608
+
609
+ if (method === "updateBatch" && !tableRules.update) {
610
+
611
+ } else if (method === "upsert" && (!tableRules.update || !tableRules.insert)) {
612
+
611
613
  } else {
612
614
  (res as any)[method] ??= true;
613
615
  }
@@ -617,9 +619,9 @@ export class PublishParser {
617
619
 
618
620
  return res;
619
621
  }
620
-
622
+
621
623
  parsed_table = getImpliedMethods(parsed_table);
622
-
624
+
623
625
  return parsed_table;
624
626
  } catch (e) {
625
627
  throw e;
@@ -627,117 +629,117 @@ export class PublishParser {
627
629
  }
628
630
 
629
631
 
630
-
632
+
631
633
  /* Prepares schema for client. Only allowed views and commands will be present */
632
- async getSchemaFromPublish(socket: any): Promise<{schema: TableSchemaForClient; tables: DBSchemaTable[] }> {
633
- let schema: TableSchemaForClient = {};
634
- let tables: DBSchemaTable[] = []
635
-
636
- try {
637
- /* Publish tables and views based on socket */
638
- const clientInfo = await this.prostgles.authHandler?.getClientInfo({ socket });
639
- let _publish = await this.getPublish(socket, clientInfo);
640
-
641
-
642
- if(_publish && Object.keys(_publish).length){
643
- let txKey = "tx";
644
- if(!this.prostgles.opts.transactions) txKey = "";
645
- if(typeof this.prostgles.opts.transactions === "string") txKey = this.prostgles.opts.transactions;
646
-
647
- const tableNames = Object.keys(_publish).filter(k => !txKey || txKey !== k);
648
-
649
- await Promise.all(tableNames
650
- .map(async tableName => {
651
- if(!this.dbo[tableName]) {
652
- throw `Table ${tableName} does not exist
634
+ async getSchemaFromPublish(socket: any): Promise<{ schema: TableSchemaForClient; tables: DBSchemaTable[] }> {
635
+ let schema: TableSchemaForClient = {};
636
+ let tables: DBSchemaTable[] = []
637
+
638
+ try {
639
+ /* Publish tables and views based on socket */
640
+ const clientInfo = await this.prostgles.authHandler?.getClientInfo({ socket });
641
+ let _publish = await this.getPublish(socket, clientInfo);
642
+
643
+
644
+ if (_publish && Object.keys(_publish).length) {
645
+ let txKey = "tx";
646
+ if (!this.prostgles.opts.transactions) txKey = "";
647
+ if (typeof this.prostgles.opts.transactions === "string") txKey = this.prostgles.opts.transactions;
648
+
649
+ const tableNames = Object.keys(_publish).filter(k => !txKey || txKey !== k);
650
+
651
+ await Promise.all(tableNames
652
+ .map(async tableName => {
653
+ if (!this.dbo[tableName]) {
654
+ throw `Table ${tableName} does not exist
653
655
  Expecting one of: ${this.prostgles.dboBuilder.tablesOrViews?.map(tov => tov.name).join(", ")}
654
656
  DBO tables: ${Object.keys(this.dbo).filter(k => (this.dbo[k] as any).find).join(", ")}
655
657
  `;
656
- }
657
-
658
- const table_rules = await this.getTableRules({ localParams: {socket}, tableName }, clientInfo);
659
-
660
- if(table_rules && Object.keys(table_rules).length){
661
- schema[tableName] = {};
662
- let methods: MethodKey[] = [];
663
- let tableInfo: TableInfo | undefined;
664
- let tableColumns: DBSchemaTable["columns"] | undefined;
665
-
666
- if(typeof table_rules === "object"){
667
- methods = getKeys(table_rules) as any;
668
- }
669
-
670
- await Promise.all(methods.filter(m => m !== "select" as any).map(async method => {
671
- if(method === "sync" && table_rules[method]){
672
-
673
- /* Pass sync info */
674
- schema[tableName][method] = table_rules[method];
675
- } else if((table_rules as any)[method]) {
676
-
677
- schema[tableName][method] = {};
678
-
679
- /* Test for issues with the common table CRUD methods () */
680
- if(TABLE_METHODS.includes(method as any)){
681
-
682
- let err = null;
683
- try {
684
- let valid_table_command_rules = await this.getValidatedRequestRule({ tableName, command: method, localParams: {socket} }, clientInfo);
685
- await (this.dbo[tableName] as any)[method]({}, {}, {}, valid_table_command_rules, { socket, isRemoteRequest: true, testRule: true });
686
-
687
- } catch(e) {
688
- err = "INTERNAL PUBLISH ERROR";
689
- schema[tableName][method] = { err };
690
-
691
- throw `publish.${tableName}.${method}: \n -> ${e}`;
692
- }
693
- }
694
-
695
-
696
- if(method === "getInfo" || method === "getColumns"){
697
- let tableRules = await this.getValidatedRequestRule({ tableName, command: method, localParams: {socket} }, clientInfo);
698
- const res = await (this.dbo[tableName] as any)[method](undefined, undefined, undefined , tableRules, { socket, isRemoteRequest: true });
699
- if(method === "getInfo"){
700
- tableInfo = res;
701
- } else if(method === "getColumns"){
702
- tableColumns = res;
703
- }
704
- }
705
- }
706
- }));
707
-
708
- if(tableInfo && tableColumns){
709
-
710
- tables.push({
711
- name: tableName,
712
- info: tableInfo,
713
- columns: tableColumns
714
- })
715
- }
716
- }
717
-
718
- return true;
719
- })
720
- );
721
- }
722
-
723
-
724
- } catch (e) {
725
- console.error("Prostgles \nERRORS IN PUBLISH: ", JSON.stringify(e));
726
- throw e;
658
+ }
659
+
660
+ const table_rules = await this.getTableRules({ localParams: { socket }, tableName }, clientInfo);
661
+
662
+ if (table_rules && Object.keys(table_rules).length) {
663
+ schema[tableName] = {};
664
+ let methods: MethodKey[] = [];
665
+ let tableInfo: TableInfo | undefined;
666
+ let tableColumns: DBSchemaTable["columns"] | undefined;
667
+
668
+ if (typeof table_rules === "object") {
669
+ methods = getKeys(table_rules) as any;
670
+ }
671
+
672
+ await Promise.all(methods.filter(m => m !== "select" as any).map(async method => {
673
+ if (method === "sync" && table_rules[method]) {
674
+
675
+ /* Pass sync info */
676
+ schema[tableName][method] = table_rules[method];
677
+ } else if ((table_rules as any)[method]) {
678
+
679
+ schema[tableName][method] = {};
680
+
681
+ /* Test for issues with the common table CRUD methods () */
682
+ if (TABLE_METHODS.includes(method as any)) {
683
+
684
+ let err = null;
685
+ try {
686
+ let valid_table_command_rules = await this.getValidatedRequestRule({ tableName, command: method, localParams: { socket } }, clientInfo);
687
+ await (this.dbo[tableName] as any)[method]({}, {}, {}, valid_table_command_rules, { socket, isRemoteRequest: true, testRule: true });
688
+
689
+ } catch (e) {
690
+ err = "INTERNAL PUBLISH ERROR";
691
+ schema[tableName][method] = { err };
692
+
693
+ throw `publish.${tableName}.${method}: \n -> ${e}`;
694
+ }
695
+ }
696
+
697
+
698
+ if (method === "getInfo" || method === "getColumns") {
699
+ let tableRules = await this.getValidatedRequestRule({ tableName, command: method, localParams: { socket } }, clientInfo);
700
+ const res = await (this.dbo[tableName] as any)[method](undefined, undefined, undefined, tableRules, { socket, isRemoteRequest: true });
701
+ if (method === "getInfo") {
702
+ tableInfo = res;
703
+ } else if (method === "getColumns") {
704
+ tableColumns = res;
705
+ }
706
+ }
707
+ }
708
+ }));
709
+
710
+ if (tableInfo && tableColumns) {
711
+
712
+ tables.push({
713
+ name: tableName,
714
+ info: tableInfo,
715
+ columns: tableColumns
716
+ })
717
+ }
718
+ }
719
+
720
+ return true;
721
+ })
722
+ );
727
723
  }
728
-
729
- return { schema, tables };
724
+
725
+
726
+ } catch (e) {
727
+ console.error("Prostgles \nERRORS IN PUBLISH: ", JSON.stringify(e));
728
+ throw e;
729
+ }
730
+
731
+ return { schema, tables };
730
732
  }
731
733
 
732
734
  }
733
735
 
734
-
735
- function applyParamsIfFunc(maybeFunc: any, ...params: any): any{
736
- if(
737
- (maybeFunc !== null && maybeFunc !== undefined) &&
738
- (typeof maybeFunc === "function" || typeof maybeFunc.then === "function")
739
- ){
740
- return maybeFunc(...params);
736
+
737
+ function applyParamsIfFunc(maybeFunc: any, ...params: any): any {
738
+ if (
739
+ (maybeFunc !== null && maybeFunc !== undefined) &&
740
+ (typeof maybeFunc === "function" || typeof maybeFunc.then === "function")
741
+ ) {
742
+ return maybeFunc(...params);
741
743
  }
742
744
 
743
745
  return maybeFunc;