prostgles-server 4.2.158 → 4.2.160

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.
Files changed (145) hide show
  1. package/dist/Auth/AuthTypes.d.ts +4 -8
  2. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  3. package/dist/Auth/setAuthProviders.d.ts.map +1 -1
  4. package/dist/Auth/setAuthProviders.js +4 -5
  5. package/dist/Auth/setAuthProviders.js.map +1 -1
  6. package/dist/Auth/setEmailProvider.js +3 -3
  7. package/dist/Auth/setEmailProvider.js.map +1 -1
  8. package/package.json +1 -1
  9. package/lib/Auth/AuthHandler.ts +0 -436
  10. package/lib/Auth/AuthTypes.ts +0 -285
  11. package/lib/Auth/getSafeReturnURL.ts +0 -35
  12. package/lib/Auth/sendEmail.ts +0 -83
  13. package/lib/Auth/setAuthProviders.ts +0 -129
  14. package/lib/Auth/setEmailProvider.ts +0 -85
  15. package/lib/Auth/setupAuthRoutes.ts +0 -161
  16. package/lib/DBEventsManager.ts +0 -178
  17. package/lib/DBSchemaBuilder.ts +0 -225
  18. package/lib/DboBuilder/DboBuilder.ts +0 -319
  19. package/lib/DboBuilder/DboBuilderTypes.ts +0 -361
  20. package/lib/DboBuilder/QueryBuilder/Functions.ts +0 -1153
  21. package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +0 -288
  22. package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +0 -263
  23. package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +0 -271
  24. package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +0 -136
  25. package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +0 -22
  26. package/lib/DboBuilder/QueryStreamer.ts +0 -250
  27. package/lib/DboBuilder/TableHandler/DataValidator.ts +0 -428
  28. package/lib/DboBuilder/TableHandler/TableHandler.ts +0 -205
  29. package/lib/DboBuilder/TableHandler/delete.ts +0 -115
  30. package/lib/DboBuilder/TableHandler/insert.ts +0 -183
  31. package/lib/DboBuilder/TableHandler/insertTest.ts +0 -78
  32. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +0 -62
  33. package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +0 -134
  34. package/lib/DboBuilder/TableHandler/update.ts +0 -126
  35. package/lib/DboBuilder/TableHandler/updateBatch.ts +0 -49
  36. package/lib/DboBuilder/TableHandler/updateFile.ts +0 -48
  37. package/lib/DboBuilder/TableHandler/upsert.ts +0 -34
  38. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +0 -393
  39. package/lib/DboBuilder/ViewHandler/count.ts +0 -38
  40. package/lib/DboBuilder/ViewHandler/find.ts +0 -153
  41. package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +0 -73
  42. package/lib/DboBuilder/ViewHandler/getExistsFilters.ts +0 -74
  43. package/lib/DboBuilder/ViewHandler/getInfo.ts +0 -32
  44. package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +0 -84
  45. package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +0 -96
  46. package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +0 -105
  47. package/lib/DboBuilder/ViewHandler/parseJoinPath.ts +0 -208
  48. package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +0 -163
  49. package/lib/DboBuilder/ViewHandler/prepareWhere.ts +0 -90
  50. package/lib/DboBuilder/ViewHandler/size.ts +0 -37
  51. package/lib/DboBuilder/ViewHandler/subscribe.ts +0 -118
  52. package/lib/DboBuilder/ViewHandler/validateViewRules.ts +0 -70
  53. package/lib/DboBuilder/dboBuilderUtils.ts +0 -222
  54. package/lib/DboBuilder/getColumns.ts +0 -114
  55. package/lib/DboBuilder/getCondition.ts +0 -201
  56. package/lib/DboBuilder/getSubscribeRelatedTables.ts +0 -190
  57. package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +0 -426
  58. package/lib/DboBuilder/insertNestedRecords.ts +0 -355
  59. package/lib/DboBuilder/parseUpdateRules.ts +0 -187
  60. package/lib/DboBuilder/prepareShortestJoinPaths.ts +0 -186
  61. package/lib/DboBuilder/runSQL.ts +0 -182
  62. package/lib/DboBuilder/runTransaction.ts +0 -50
  63. package/lib/DboBuilder/sqlErrCodeToMsg.ts +0 -254
  64. package/lib/DboBuilder/uploadFile.ts +0 -69
  65. package/lib/Event_Trigger_Tags.ts +0 -118
  66. package/lib/FileManager/FileManager.ts +0 -358
  67. package/lib/FileManager/getValidatedFileType.ts +0 -69
  68. package/lib/FileManager/initFileManager.ts +0 -187
  69. package/lib/FileManager/upload.ts +0 -62
  70. package/lib/FileManager/uploadStream.ts +0 -79
  71. package/lib/Filtering.ts +0 -463
  72. package/lib/JSONBValidation/validate_jsonb_schema_sql.ts +0 -502
  73. package/lib/JSONBValidation/validation.ts +0 -143
  74. package/lib/Logging.ts +0 -127
  75. package/lib/PostgresNotifListenManager.ts +0 -143
  76. package/lib/Prostgles.ts +0 -485
  77. package/lib/ProstglesTypes.ts +0 -196
  78. package/lib/PubSubManager/PubSubManager.ts +0 -609
  79. package/lib/PubSubManager/addSub.ts +0 -138
  80. package/lib/PubSubManager/addSync.ts +0 -141
  81. package/lib/PubSubManager/getCreatePubSubManagerError.ts +0 -72
  82. package/lib/PubSubManager/getPubSubManagerInitQuery.ts +0 -662
  83. package/lib/PubSubManager/initPubSubManager.ts +0 -79
  84. package/lib/PubSubManager/notifListener.ts +0 -173
  85. package/lib/PubSubManager/orphanTriggerCheck.ts +0 -70
  86. package/lib/PubSubManager/pushSubData.ts +0 -55
  87. package/lib/PublishParser/PublishParser.ts +0 -162
  88. package/lib/PublishParser/getFileTableRules.ts +0 -124
  89. package/lib/PublishParser/getSchemaFromPublish.ts +0 -141
  90. package/lib/PublishParser/getTableRulesWithoutFileTable.ts +0 -177
  91. package/lib/PublishParser/publishTypesAndUtils.ts +0 -399
  92. package/lib/RestApi.ts +0 -127
  93. package/lib/SchemaWatch/SchemaWatch.ts +0 -90
  94. package/lib/SchemaWatch/createSchemaWatchEventTrigger.ts +0 -3
  95. package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +0 -45
  96. package/lib/SchemaWatch/getWatchSchemaTagList.ts +0 -27
  97. package/lib/SyncReplication.ts +0 -557
  98. package/lib/TableConfig/TableConfig.ts +0 -468
  99. package/lib/TableConfig/getColumnDefinitionQuery.ts +0 -111
  100. package/lib/TableConfig/getConstraintDefinitionQueries.ts +0 -95
  101. package/lib/TableConfig/getFutureTableSchema.ts +0 -64
  102. package/lib/TableConfig/getPGIndexes.ts +0 -53
  103. package/lib/TableConfig/getTableColumnQueries.ts +0 -129
  104. package/lib/TableConfig/initTableConfig.ts +0 -326
  105. package/lib/index.ts +0 -13
  106. package/lib/initProstgles.ts +0 -319
  107. package/lib/onSocketConnected.ts +0 -102
  108. package/lib/runClientRequest.ts +0 -129
  109. package/lib/shortestPath.ts +0 -122
  110. package/lib/typeTests/DBoGenerated.d.ts +0 -320
  111. package/lib/typeTests/dboTypeCheck.ts +0 -81
  112. package/lib/utils.ts +0 -15
  113. package/tests/client/hooks.spec.ts +0 -205
  114. package/tests/client/index.ts +0 -139
  115. package/tests/client/package-lock.json +0 -637
  116. package/tests/client/package.json +0 -26
  117. package/tests/client/renderReactHook.ts +0 -177
  118. package/tests/client/tsconfig.json +0 -15
  119. package/tests/client/useProstgles.spec.ts +0 -120
  120. package/tests/clientFileTests.spec.ts +0 -102
  121. package/tests/clientOnlyQueries.spec.ts +0 -667
  122. package/tests/clientRestApi.spec.ts +0 -82
  123. package/tests/config_test/DBoGenerated.d.ts +0 -407
  124. package/tests/config_test/index.html +0 -109
  125. package/tests/config_test/index.js +0 -86
  126. package/tests/config_test/index.js.map +0 -1
  127. package/tests/config_test/index.ts +0 -91
  128. package/tests/config_test/init.sql +0 -48
  129. package/tests/config_test/package.json +0 -29
  130. package/tests/config_test/tsconfig.json +0 -23
  131. package/tests/config_testDBoGenerated.d.ts +0 -407
  132. package/tests/isomorphicQueries.spec.ts +0 -1493
  133. package/tests/server/DBoGenerated.d.ts +0 -537
  134. package/tests/server/index.html +0 -73
  135. package/tests/server/index.ts +0 -289
  136. package/tests/server/init.sql +0 -224
  137. package/tests/server/package-lock.json +0 -2164
  138. package/tests/server/package.json +0 -25
  139. package/tests/server/publishTypeCheck.ts +0 -136
  140. package/tests/server/server.ts +0 -35
  141. package/tests/server/testPublish.ts +0 -147
  142. package/tests/server/testTableConfig.ts +0 -156
  143. package/tests/server/tsconfig.json +0 -22
  144. package/tests/serverOnlyQueries.spec.ts +0 -32
  145. package/tests/test.sh +0 -20
@@ -1,428 +0,0 @@
1
- import { AnyObject, ColumnInfo, FieldFilter, ValidatedColumnInfo, asName, getKeys, isEmpty, isObject, pickKeys, unpatchText } from "prostgles-types/dist";
2
- import { ValidateRowBasic } from "../../PublishParser/PublishParser";
3
- import { DBHandlerServer } from "../../Prostgles";
4
- import { asValue } from "../../PubSubManager/PubSubManager";
5
- import { LocalParams, TableSchemaColumn, pgp } from "../DboBuilder";
6
- import { TableHandler, ValidatedParams } from "./TableHandler";
7
- import { parseFunctionObject } from "../QueryBuilder/QueryBuilder";
8
- import { validateObj } from "../ViewHandler/ViewHandler";
9
-
10
- type RowFieldDataPlain = {
11
- type: "plain";
12
- column: TableSchemaColumn;
13
- fieldValue: any;
14
- }
15
-
16
- type RowFieldDataFunction = {
17
- type: "function";
18
- column: TableSchemaColumn;
19
- funcName: string;
20
- args: any[];
21
- }
22
- type RowFieldData = RowFieldDataPlain | RowFieldDataFunction;
23
-
24
- type ParsedRowFieldData = {
25
- escapedCol: string;
26
- escapedVal: string;
27
- };
28
-
29
- type ParseDataArgs = {
30
- rows: AnyObject[];
31
- allowedCols: string[];
32
- dbTx: DBHandlerServer;
33
- command: "update" | "insert";
34
- validationOptions: {
35
- localParams: undefined | LocalParams;
36
- validate: undefined | ValidateRowBasic;
37
- }
38
- }
39
-
40
- export class DataValidator {
41
- rowFieldData?: RowFieldData[][];
42
- parsedRowFieldData?: ParsedRowFieldData[][];
43
- tableHandler: TableHandler;
44
- constructor(tableHandler: TableHandler) {
45
- this.tableHandler = tableHandler;
46
- }
47
-
48
- parse = async (args: ParseDataArgs) => {
49
- const { command } = args;
50
- const rowFieldData = await getValidatedRowFieldData(args, this.tableHandler);
51
- const parsedRowFieldData = await getParsedRowFieldData(rowFieldData, args);
52
- if (command === "update") {
53
- if (rowFieldData.some(rowParts => rowParts.length === 0)) {
54
- throw "Empty row. No data provided for update";
55
- }
56
- }
57
-
58
- return {
59
- parsedRowFieldData,
60
- getQuery: () => getQuery(command, parsedRowFieldData, this.tableHandler.escapedName),
61
- }
62
- }
63
- }
64
-
65
- const getQuery = (type: "insert" | "update", parsedRowFieldData: ParsedRowFieldData[][], escapedTableName: string): string => {
66
- if (type === "insert") {
67
-
68
- const uniqueColumns = Array.from(new Set(parsedRowFieldData.flatMap(row => row.map(r => r.escapedCol))))
69
- const values = parsedRowFieldData.map(row => `(${uniqueColumns.map(colName => row.find(r => r.escapedCol === colName)?.escapedVal ?? 'DEFAULT')})`).join(",\n");
70
- const whatToInsert = !uniqueColumns.length ? "DEFAULT VALUES" : `(${uniqueColumns}) VALUES ${values}`
71
- return `INSERT INTO ${escapedTableName} ${whatToInsert} `;
72
- } else {
73
- const query = parsedRowFieldData.map(rowParts => {
74
- return `UPDATE ${escapedTableName} SET ` + rowParts.map(r => `${r.escapedCol} = ${r.escapedVal} `).join(",\n")
75
- }).join(";\n") + " ";
76
-
77
- return query;
78
- }
79
- }
80
-
81
- type PrepareFieldValuesArgs = {
82
- row: AnyObject | undefined;
83
- forcedData: AnyObject | undefined;
84
- allowedCols: FieldFilter | undefined;
85
- removeDisallowedColumns?: boolean;
86
- tableHandler: TableHandler;
87
- }
88
- /**
89
- * Apply forcedData, remove disallowed columns, validate against allowed columns:
90
- * @example ({ item_id: 1 }, { user_id: 32 }) => { item_id: 1, user_id: 32 }
91
- * OR
92
- * ({ a: 1 }, { b: 32 }, ["c", "d"]) => throw "a field is not allowed"
93
- * @param {Object} obj - initial data
94
- * @param {Object} forcedData - set/override property
95
- * @param {string[]} allowed_cols - allowed columns (excluding forcedData) from table rules
96
- */
97
- const getValidatedRow = ({ row = {}, forcedData = {}, allowedCols, removeDisallowedColumns = false, tableHandler }: PrepareFieldValuesArgs): AnyObject => {
98
- const column_names = tableHandler.column_names.slice(0);
99
- if (!column_names.length) {
100
- throw "table column_names mising";
101
- }
102
- const validatedAllowedColumns = tableHandler.parseFieldFilter(allowedCols, false);
103
-
104
- let finalRow = { ...row };
105
- if (removeDisallowedColumns && !isEmpty(finalRow)) {
106
- finalRow = pickKeys(finalRow, validatedAllowedColumns);
107
- }
108
-
109
- /* If has keys check against allowed_cols */
110
- validateObj(finalRow, validatedAllowedColumns)
111
-
112
- /** Apply forcedData */
113
- if (!isEmpty(forcedData)) {
114
- finalRow = { ...finalRow, ...forcedData };
115
- }
116
-
117
- /** Validate forcedData */
118
- validateObj(finalRow, column_names.slice(0));
119
- return finalRow;
120
- }
121
-
122
- /**
123
- * Add synced_field value if missing
124
- * prepareFieldValues(): Apply forcedData, remove disallowed columns, validate against allowed columns
125
- * tableConfigurator?.checkColVal(): Validate column min/max/isText/lowerCased/trimmed values
126
- */
127
- export const prepareNewData = async ({ row, forcedData, allowedFields, tableRules, fixIssues = false, tableConfigurator, tableHandler }: ValidatedParams) => {
128
- const synced_field = (tableRules ?? {})?.sync?.synced_field;
129
-
130
- /* Update synced_field if sync is on and missing */
131
- if (synced_field && !row[synced_field]) {
132
- row[synced_field] = Date.now();
133
- }
134
-
135
- const data = getValidatedRow({ tableHandler, row, forcedData, allowedCols: allowedFields , removeDisallowedColumns: fixIssues });
136
- const dataKeys = getKeys(data);
137
-
138
- dataKeys.forEach(col => {
139
- tableConfigurator?.checkColVal({ table: tableHandler.name, col, value: data[col] });
140
- const colConfig = tableConfigurator?.getColumnConfig(tableHandler.name, col);
141
- if (colConfig && isObject(colConfig) && "isText" in colConfig && data[col]) {
142
- if (colConfig.lowerCased) {
143
- data[col] = data[col].toString().toLowerCase()
144
- }
145
- if (colConfig.trimmed) {
146
- data[col] = data[col].toString().trim()
147
- }
148
- }
149
- })
150
-
151
- const allowedCols = tableHandler.columns.filter(c => dataKeys.includes(c.name)).map(c => c.name);
152
- return { data, allowedCols }
153
- }
154
-
155
-
156
- /**
157
- * Ensures:
158
- * - allowedCols are valid and checked against data
159
- * - validate()
160
- * - update is not empty
161
- * - no duplicate column names ( could update with $func and plain value for same column )
162
- */
163
- const getValidatedRowFieldData = async ({ allowedCols, rows, validationOptions, dbTx, command }: ParseDataArgs, tableHandler: TableHandler) => {
164
- if (!allowedCols.length && command === "update") {
165
- throw "allowedColumns cannot be empty";
166
- }
167
- const rowFieldData = await Promise.all(
168
- rows.map(async nonvalidatedRow => {
169
-
170
- let row = pickKeys(nonvalidatedRow, allowedCols);
171
- const initialRowKeys = Object.keys(row);
172
- if (validationOptions.validate) {
173
- if(!validationOptions.localParams){
174
- throw "localParams missing for validate";
175
- }
176
- row = await validationOptions.validate({ row, dbx: dbTx, localParams: validationOptions.localParams });
177
- }
178
- const keysAddedDuringValidate = Object.keys(row).filter(newKey => !initialRowKeys.includes(newKey));
179
-
180
- const getColumn = (fieldName: string) => {
181
- if (!allowedCols.concat(keysAddedDuringValidate).includes(fieldName)) {
182
- throw `Unexpected/Dissallowed column name: ${fieldName}`;
183
- }
184
- const column = tableHandler.columns.find(c => c.name === fieldName);
185
- if (!column) {
186
- throw `Invalid column: ${fieldName}`;
187
- }
188
- return column;
189
- };
190
-
191
- const rowPartValues = Object.entries(row).map(([fieldName, fieldValue]) => {
192
- const column = getColumn(fieldName);
193
- if (isObject(fieldValue)) {
194
-
195
- // const textPatch = getTextPatch(column, fieldValue);
196
- // if(textPatch){
197
- // return {
198
- // type: "plain",
199
- // column,
200
- // fieldValue: textPatch,
201
- // } satisfies RowFieldData;
202
- // }
203
-
204
- const [firstKey, ...otherkeys] = Object.keys(fieldValue);
205
- const func = firstKey && !otherkeys.length? convertionFuncs.some(f => `$${f.name}` === firstKey) : undefined;
206
- if(func){
207
- const { funcName, args } = parseFunctionObject(fieldValue);
208
- return {
209
- type: "function",
210
- column,
211
- funcName,
212
- args,
213
- } satisfies RowFieldData
214
- }
215
- }
216
- return {
217
- type: "plain",
218
- column: getColumn(fieldName),
219
- fieldValue,
220
- } satisfies RowFieldData;
221
- });
222
-
223
- return rowPartValues;
224
- }));
225
-
226
- return rowFieldData;
227
- }
228
-
229
- const getTextPatch = async (c: TableSchemaColumn, fieldValue: any) => {
230
-
231
- if (c.data_type === "text" && fieldValue && isObject(fieldValue) && !["from", "to"].find(key => typeof fieldValue[key] !== "number")) {
232
- const unrecProps = Object.keys(fieldValue).filter(k => !["from", "to", "text", "md5"].includes(k));
233
- if (unrecProps.length) {
234
- throw "Unrecognised params in textPatch field: " + unrecProps.join(", ");
235
- }
236
- const patchedTextData: {
237
- fieldName: string;
238
- from: number;
239
- to: number;
240
- text: string;
241
- md5: string
242
- } = {
243
- ...fieldValue,
244
- fieldName: c.name
245
- } as any;
246
-
247
- // if (tableRules && !tableRules.select) throw "Select needs to be permitted to patch data";
248
- // const rows = await this.find(filter, { select: patchedTextData.reduce((a, v) => ({ ...a, [v.fieldName]: 1 }), {}) }, undefined, tableRules);
249
-
250
- // if (rows.length !== 1) {
251
- // throw "Cannot patch data within a filter that affects more/less than 1 row";
252
- // }
253
- // return unpatchText(rows[0][p.fieldName], patchedTextData);
254
- const rawValue = `OVERLAY(${asName(c.name)} PLACING ${asValue(patchedTextData.text)} FROM ${asValue(patchedTextData.from)} FOR ${asValue(patchedTextData.to - patchedTextData.from + 1)})`
255
- return rawValue;
256
- }
257
-
258
- return undefined
259
- }
260
-
261
- const getParsedRowFieldDataFunction = async (rowPart: RowFieldDataFunction, args: ParseDataArgs) => {
262
-
263
- const func = convertionFuncs.find(f => `$${f.name}` === rowPart.funcName);
264
- if (!func) {
265
- throw `Unknown function: ${rowPart.funcName}. Expecting one of: ${convertionFuncs.map(f => f.name).join(", ")}`;
266
- }
267
- if (func.onlyAllowedFor && func.onlyAllowedFor !== args.command) {
268
- throw `Function ${rowPart.funcName} is only allowed for ${func.onlyAllowedFor} but not ${args.command}`;
269
- }
270
- return func.getQuery(rowPart);
271
- };
272
-
273
- const getParsedRowFieldData = async (rowFieldData: RowFieldData[][], args: ParseDataArgs) => {
274
- const parsedRowFieldData = Promise.all(rowFieldData.map(rowParts => {
275
- return Promise.all(rowParts.map(async rowPart => {
276
- let escapedVal: string;
277
- if (rowPart.type === "function") {
278
- escapedVal = await getParsedRowFieldDataFunction(rowPart, args);
279
- } else {
280
-
281
- /** Prevent pg-promise formatting jsonb */
282
- const colIsJSON = ["json", "jsonb"].includes(rowPart.column.data_type);
283
- escapedVal = pgp.as.format(colIsJSON ? "$1:json" : "$1", [rowPart.fieldValue])
284
- }
285
-
286
- /**
287
- * Cast to type to avoid array errors (they do not cast automatically)
288
- */
289
- escapedVal += `::${rowPart.column.udt_name}`;
290
-
291
- return {
292
- escapedCol: asName(rowPart.column.name),
293
- escapedVal,
294
- };
295
- }));
296
- }));
297
-
298
- return parsedRowFieldData;
299
- }
300
-
301
-
302
-
303
- type ConvertionFunc = {
304
- name: string;
305
- description?: string;
306
- onlyAllowedFor?: "insert" | "update";
307
- getQuery: (fieldPart: RowFieldDataFunction) => string;
308
- };
309
-
310
- const convertionFuncs: ConvertionFunc[] = [
311
- ...[
312
- "ST_GeomFromText",
313
- "ST_Point",
314
- "ST_MakePoint",
315
- "ST_MakePointM",
316
- "ST_PointFromText",
317
- "ST_GeomFromEWKT",
318
- "ST_GeomFromGeoJSON"
319
- ].map(name => ({
320
- name,
321
- getQuery: ({ args }) => {
322
- const argList = args.map(arg => asValue(arg)).join(", ");
323
- return `${name}(${argList})`;
324
- }
325
- } satisfies ConvertionFunc)),
326
- {
327
- name: "to_timestamp",
328
- getQuery: ({ args }) => `to_timestamp(${asValue(args[0])}::BIGINT/1000.0)::timestamp`
329
- }, {
330
- name: "merge",
331
- description: "Merge the provided jsonb objects into the existing column value, ensuring that a null source value will be coalesced with provided values",
332
- onlyAllowedFor: "update",
333
- getQuery: ({ args, column }) => {
334
- if (!args.length) throw "merge function requires at least one argument";
335
- const argVals = args.map(arg => asValue(arg))
336
- const argList = argVals.join(" || ");
337
- return `COALESCE(${asName(column.name)}, ${argVals.join(", ")}) || ${argList}`;
338
- }
339
- }
340
- ];
341
-
342
-
343
- export class ColSet {
344
- opts: {
345
- columns: ColumnInfo[];
346
- tableName: string;
347
- colNames: string[];
348
- };
349
-
350
- constructor(columns: ColumnInfo[], tableName: string) {
351
- this.opts = { columns, tableName, colNames: columns.map(c => c.name) }
352
- }
353
-
354
-
355
- // private async getRow(data: any, allowedCols: string[], dbTx: DBHandlerServer, validate: ValidateRow | undefined, command: "update" | "insert", localParams: LocalParams | undefined): Promise<ParsedRowFieldData[]> {
356
- // const badCol = allowedCols.find(c => !this.opts.colNames.includes(c))
357
- // if (!allowedCols || badCol) {
358
- // throw "Missing or unexpected columns: " + badCol;
359
- // }
360
-
361
- // if (command === "update" && isEmpty(data)) {
362
- // throw "No data provided for update";
363
- // }
364
-
365
- // let row = pickKeys(data, allowedCols);
366
- // if (validate) {
367
- // if (!localParams) throw "localParams missing"
368
- // row = await validate({ row, dbx: dbTx, localParams });
369
- // }
370
-
371
- // return Object.entries(row).map(([fieldName, fieldValue]) => {
372
- // const col = this.opts.columns.find(c => c.name === fieldName);
373
- // if (!col) throw "Unexpected missing col name";
374
-
375
- // /**
376
- // * Add conversion functions for PostGIS data
377
- // */
378
- // let escapedVal = "";
379
- // if ((col.udt_name === "geometry" || col.udt_name === "geography") && isObject(fieldValue)) {
380
-
381
- // const dataKeys = Object.keys(fieldValue);
382
- // const funcName = dataKeys[0]!;
383
- // const func = convertionFuncs.find(f => f.name === funcName);
384
- // const funcArgs = fieldValue?.[funcName]
385
- // if (dataKeys.length !== 1 || !func || !Array.isArray(funcArgs)) {
386
- // throw `Expecting only one function key (${convertionFuncs.join(", ")}) \nwith an array of arguments \n within column (${fieldName}) data but got: ${JSON.stringify(fieldValue)} \nExample: { geo_col: { ST_GeomFromText: ["POINT(-71.064544 42.28787)", 4326] } }`;
387
- // }
388
- // escapedVal = func.getQuery(funcArgs);
389
- // } else if (col.udt_name === "text") {
390
-
391
-
392
- // } else {
393
- // /** Prevent pg-promise formatting jsonb */
394
- // const colIsJSON = ["json", "jsonb"].includes(col.data_type);
395
- // escapedVal = pgp.as.format(colIsJSON ? "$1:json" : "$1", [fieldValue])
396
- // }
397
-
398
- // /**
399
- // * Cast to type to avoid array errors (they do not cast automatically)
400
- // */
401
- // escapedVal += `::${col.udt_name}`
402
-
403
- // return {
404
- // escapedCol: asName(fieldName),
405
- // escapedVal,
406
- // }
407
- // });
408
-
409
- // }
410
-
411
- // async getInsertQuery(data: AnyObject[], allowedCols: string[], dbTx: DBHandlerServer, validate: ValidateRowBasic | undefined, localParams: LocalParams | undefined) {
412
- // const inserts = (await Promise.all(data.map(async d => {
413
- // const rowParts = await this.getRow(d, allowedCols, dbTx, validate, "insert", localParams);
414
- // return Object.fromEntries(rowParts.map(rp => [rp.escapedCol, rp.escapedVal]));
415
- // })));
416
- // const uniqueColumns = Array.from(new Set(inserts.flatMap(row => Object.keys(row))))
417
- // const values = inserts.map(row => `(${uniqueColumns.map(colName => row[colName] ?? 'DEFAULT')})`).join(",\n");
418
- // const whatToInsert = !uniqueColumns.length ? "DEFAULT VALUES" : `(${uniqueColumns}) VALUES ${values}`
419
- // return `INSERT INTO ${this.opts.tableName} ${whatToInsert} `;
420
- // }
421
- // async getUpdateQuery(data: AnyObject | AnyObject[], allowedCols: string[], dbTx: DBHandlerServer, validate: ValidateRowBasic | undefined, localParams: LocalParams | undefined): Promise<string> {
422
- // const res = (await Promise.all((Array.isArray(data) ? data : [data]).map(async d => {
423
- // const rowParts = await this.getRow(d, allowedCols, dbTx, validate, "update", localParams);
424
- // return `UPDATE ${this.opts.tableName} SET ` + rowParts.map(r => `${r.escapedCol} = ${r.escapedVal} `).join(",\n")
425
- // }))).join(";\n") + " ";
426
- // return res;
427
- // }
428
- }
@@ -1,205 +0,0 @@
1
- import pgPromise from "pg-promise";
2
- import { AnyObject, asName, DeleteParams, FieldFilter, InsertParams, Select, UpdateParams } from "prostgles-types";
3
- import { DB } from "../../Prostgles";
4
- import { SyncRule, TableRule } from "../../PublishParser/PublishParser";
5
- import TableConfigurator from "../../TableConfig/TableConfig";
6
- import { DboBuilder, Filter, getErrorAsObject, getSerializedClientErrorFromPGError, LocalParams, TableHandlers } from "../DboBuilder";
7
- import type { TableSchema } from "../DboBuilderTypes";
8
- import { parseUpdateRules } from "../parseUpdateRules";
9
- import { COMPUTED_FIELDS, FUNCTIONS } from "../QueryBuilder/Functions";
10
- import { SelectItem, SelectItemBuilder } from "../QueryBuilder/QueryBuilder";
11
- import { JoinPaths, ViewHandler } from "../ViewHandler/ViewHandler";
12
- import { DataValidator } from "./DataValidator";
13
- import { _delete } from "./delete";
14
- import { insert } from "./insert";
15
- import { update } from "./update";
16
- import { updateBatch } from "./updateBatch";
17
- import { upsert } from "./upsert";
18
-
19
-
20
- export type ValidatedParams = {
21
- row: AnyObject;
22
- forcedData?: AnyObject;
23
- allowedFields?: FieldFilter;
24
- tableRules?: TableRule;
25
- fixIssues: boolean;
26
- tableConfigurator: TableConfigurator | undefined;
27
- tableHandler: TableHandler;
28
- }
29
-
30
- export class TableHandler extends ViewHandler {
31
-
32
- dataValidator: DataValidator;
33
- constructor(db: DB, tableOrViewInfo: TableSchema, dboBuilder: DboBuilder, tx?: {t: pgPromise.ITask<{}>, dbTX: TableHandlers}, joinPaths?: JoinPaths) {
34
- super(db, tableOrViewInfo, dboBuilder, tx, joinPaths);
35
-
36
- this.remove = this.delete;
37
-
38
- this.dataValidator = new DataValidator(this);
39
- this.is_view = false;
40
- this.is_media = dboBuilder.prostgles.isMedia(this.name)
41
- }
42
-
43
- getFinalDBtx = (localParams: LocalParams | undefined) => {
44
- return localParams?.tx?.dbTX ?? this.tx?.dbTX;
45
- }
46
- getFinalDbo = (localParams: LocalParams | undefined) => {
47
- return this.getFinalDBtx(localParams) ?? this.dboBuilder.dbo;
48
- }
49
-
50
- parseUpdateRules = parseUpdateRules.bind(this);
51
-
52
- update = update.bind(this);
53
- updateBatch = updateBatch.bind(this);
54
-
55
- async insert(
56
- rowOrRows: AnyObject | AnyObject[],
57
- param2?: InsertParams,
58
- param3_unused?: undefined,
59
- tableRules?: TableRule,
60
- _localParams?: LocalParams
61
- ): Promise<any | any[] | boolean> {
62
- return insert.bind(this)(rowOrRows, param2, param3_unused, tableRules, _localParams)
63
- }
64
-
65
- prepareReturning = async (returning: Select | undefined, allowedFields: string[]): Promise<SelectItem[]> => {
66
- const result: SelectItem[] = [];
67
- if (returning) {
68
- const sBuilder = new SelectItemBuilder({
69
- allFields: this.column_names.slice(0),
70
- allowedFields,
71
- allowedOrderByFields: allowedFields,
72
- computedFields: COMPUTED_FIELDS,
73
- functions: FUNCTIONS.filter(f => f.type === "function" && f.singleColArg),
74
- isView: this.is_view,
75
- columns: this.columns,
76
- });
77
- await sBuilder.parseUserSelect(returning);
78
-
79
- return sBuilder.select;
80
- }
81
-
82
- return result;
83
- }
84
-
85
- makeReturnQuery(items?: SelectItem[]) {
86
- if (items?.length) return " RETURNING " + getSelectItemQuery(items);
87
- return "";
88
- }
89
-
90
- async delete(filter?: Filter, params?: DeleteParams, param3_unused?: undefined, table_rules?: TableRule, localParams?: LocalParams): Promise<any> {
91
- return _delete.bind(this)(filter, params, param3_unused, table_rules, localParams);
92
- }
93
-
94
- remove(filter: Filter, params?: UpdateParams, param3_unused?: undefined, tableRules?: TableRule, localParams?: LocalParams) {
95
- return this.delete(filter, params, param3_unused, tableRules, localParams);
96
- }
97
-
98
- upsert = upsert.bind(this);
99
-
100
- /* External request. Cannot sync from server */
101
- async sync(filter: Filter, params: { select?: FieldFilter }, param3_unused: undefined, table_rules: TableRule, localParams: LocalParams) {
102
- const start = Date.now();
103
- try {
104
-
105
- if(!this.dboBuilder.canSubscribe){
106
- throw "Cannot subscribe. PubSubManager not initiated";
107
- }
108
-
109
- if (!localParams) throw "Sync not allowed within the server code";
110
- const { socket } = localParams;
111
- if (!socket) throw "socket missing";
112
-
113
-
114
- if (!table_rules || !table_rules.sync || !table_rules.select) throw "sync or select table rules missing";
115
-
116
- if (this.tx) throw "Sync not allowed within transactions";
117
-
118
- const ALLOWED_PARAMS = ["select"];
119
- const invalidParams = Object.keys(params || {}).filter(k => !ALLOWED_PARAMS.includes(k));
120
- if (invalidParams.length) throw "Invalid or dissallowed params found: " + invalidParams.join(", ");
121
-
122
- const { synced_field, allow_delete }: SyncRule = table_rules.sync;
123
-
124
- if (!table_rules.sync.id_fields.length || !synced_field) {
125
- const err = "INTERNAL ERROR: id_fields OR synced_field missing from publish";
126
- console.error(err);
127
- throw err;
128
- }
129
-
130
- const id_fields = this.parseFieldFilter(table_rules.sync.id_fields, false);
131
- const syncFields = [...id_fields, synced_field];
132
-
133
- const allowedSelect = this.parseFieldFilter(table_rules?.select.fields ?? false);
134
- if (syncFields.find(f => !allowedSelect.includes(f))) {
135
- throw `INTERNAL ERROR: sync field missing from publish.${this.name}.select.fields`;
136
- }
137
- const select = this.getAllowedSelectFields(
138
- params?.select ?? "*",
139
- allowedSelect,
140
- false
141
- );
142
- if (!select.length) throw "Empty select not allowed";
143
-
144
- /* Add sync fields if missing */
145
- syncFields.map(sf => {
146
- if (!select.includes(sf)) select.push(sf);
147
- });
148
-
149
- /* Step 1: parse command and params */
150
- const result = await this.find(filter, { select, limit: 0 }, undefined, table_rules, localParams)
151
- .then(async _isValid => {
152
-
153
- const { filterFields, forcedFilter } = table_rules?.select || {};
154
- const condition = (await this.prepareWhere({ select: undefined, filter, forcedFilter, filterFields, addWhere: false, localParams, tableRule: table_rules })).where;
155
-
156
- const pubSubManager = await this.dboBuilder.getPubSubManager();
157
- return pubSubManager.addSync({
158
- table_info: this.tableOrViewInfo,
159
- condition,
160
- id_fields,
161
- synced_field,
162
- allow_delete,
163
- socket,
164
- table_rules,
165
- filter: { ...filter },
166
- params: { select }
167
- }).then(channelName => ({ channelName, id_fields, synced_field }));
168
- });
169
- await this._log({ command: "sync", localParams, data: { filter, params }, duration: Date.now() - start });
170
- return result;
171
- } catch (e) {
172
- await this._log({ command: "sync", localParams, data: { filter, params }, duration: Date.now() - start, error: getErrorAsObject(e) });
173
- throw getSerializedClientErrorFromPGError(e, { type: "tableMethod", localParams, view: this });
174
- }
175
-
176
- /*
177
- REPLICATION
178
-
179
- 1 Sync proccess (NO DELETES ALLOWED):
180
-
181
- Client sends:
182
- "sync-request"
183
- { min_id, max_id, count, max_synced }
184
-
185
- Server sends:
186
- "sync-pull"
187
- { from_synced }
188
-
189
- Client sends:
190
- "sync-push"
191
- { data } -> WHERE synced >= from_synced
192
-
193
- Server upserts:
194
- WHERE not exists synced = synced AND id = id
195
- UNTIL
196
-
197
- Server sends
198
- "sync-push"
199
- { data } -> WHERE synced >= from_synced
200
- */
201
- }
202
-
203
- }
204
-
205
- export const getSelectItemQuery = (items: SelectItem[]) => items.map(s => s.getQuery() + " AS " + asName(s.alias)).join(", ")