meadow 2.0.23 → 2.0.27
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/README.md +110 -141
- package/docs/README.md +34 -230
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +44 -12
- package/docs/_topbar.md +5 -0
- package/docs/api/doCount.md +109 -0
- package/docs/api/doCreate.md +132 -0
- package/docs/api/doDelete.md +101 -0
- package/docs/api/doRead.md +122 -0
- package/docs/api/doReads.md +136 -0
- package/docs/api/doUndelete.md +98 -0
- package/docs/api/doUpdate.md +129 -0
- package/docs/api/getRoleName.md +84 -0
- package/docs/api/loadFromPackage.md +153 -0
- package/docs/api/marshalRecordFromSourceToObject.md +92 -0
- package/docs/api/query.md +133 -0
- package/docs/api/rawQueries.md +197 -0
- package/docs/api/reference.md +117 -0
- package/docs/api/setAuthorizer.md +103 -0
- package/docs/api/setDefault.md +90 -0
- package/docs/api/setDefaultIdentifier.md +84 -0
- package/docs/api/setDomain.md +56 -0
- package/docs/api/setIDUser.md +91 -0
- package/docs/api/setJsonSchema.md +92 -0
- package/docs/api/setProvider.md +87 -0
- package/docs/api/setSchema.md +107 -0
- package/docs/api/setScope.md +68 -0
- package/docs/api/validateObject.md +119 -0
- package/docs/architecture.md +316 -0
- package/docs/audit-tracking.md +226 -0
- package/docs/configuration.md +317 -0
- package/docs/providers/meadow-endpoints.md +306 -0
- package/docs/providers/mongodb.md +319 -0
- package/docs/providers/postgresql.md +312 -0
- package/docs/providers/rocksdb.md +297 -0
- package/docs/query-dsl.md +269 -0
- package/docs/quick-start.md +384 -0
- package/docs/raw-queries.md +193 -0
- package/docs/retold-catalog.json +61 -1
- package/docs/retold-keyword-index.json +15860 -4839
- package/docs/soft-deletes.md +224 -0
- package/package.json +44 -13
- package/scripts/bookstore-seed-postgresql.sql +135 -0
- package/scripts/dgraph-test-db.sh +144 -0
- package/scripts/meadow-test-cleanup.sh +5 -1
- package/scripts/mongodb-test-db.sh +98 -0
- package/scripts/postgresql-test-db.sh +124 -0
- package/scripts/solr-test-db.sh +135 -0
- package/source/Meadow.js +5 -0
- package/source/providers/Meadow-Provider-DGraph.js +679 -0
- package/source/providers/Meadow-Provider-MeadowEndpoints.js +1 -1
- package/source/providers/Meadow-Provider-MongoDB.js +527 -0
- package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
- package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
- package/source/providers/Meadow-Provider-Solr.js +726 -0
- package/test/Meadow-Provider-DGraph_tests.js +741 -0
- package/test/Meadow-Provider-MongoDB_tests.js +661 -0
- package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
- package/test/Meadow-Provider-RocksDB_tests.js +887 -0
- package/test/Meadow-Provider-SQLiteBrowser-Headless_tests.js +657 -0
- package/test/Meadow-Provider-SQLiteBrowser_tests.js +895 -0
- package/test/Meadow-Provider-Solr_tests.js +679 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// ##### Part of the **[retold](https://stevenvelozo.github.io/retold/)** system
|
|
2
|
+
/**
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @author <steven@velozo.com>
|
|
5
|
+
*/
|
|
6
|
+
var MeadowProvider = function ()
|
|
7
|
+
{
|
|
8
|
+
function createNew(pFable)
|
|
9
|
+
{
|
|
10
|
+
// If a valid Fable object isn't passed in, return a constructor
|
|
11
|
+
if (typeof (pFable) !== 'object')
|
|
12
|
+
{
|
|
13
|
+
return { new: createNew };
|
|
14
|
+
}
|
|
15
|
+
var _Fable = pFable;
|
|
16
|
+
var _GlobalLogLevel = 0;
|
|
17
|
+
if (_Fable.settings.PostgreSQL)
|
|
18
|
+
{
|
|
19
|
+
_GlobalLogLevel = _Fable.settings.PostgreSQL.GlobalLogLevel || 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convert named parameters (:paramName) to positional ($N) parameters
|
|
24
|
+
* for the pg library.
|
|
25
|
+
*
|
|
26
|
+
* @param {String} pQueryBody - SQL query body with :paramName placeholders
|
|
27
|
+
* @param {Object} pNamedParams - Object of parameter name -> value mappings
|
|
28
|
+
* @return {Object} { text: String, values: Array }
|
|
29
|
+
*/
|
|
30
|
+
var convertNamedToPositional = function (pQueryBody, pNamedParams)
|
|
31
|
+
{
|
|
32
|
+
var tmpValues = [];
|
|
33
|
+
var tmpParamIndex = 0;
|
|
34
|
+
// Track which named params we've already mapped to a positional index
|
|
35
|
+
var tmpParamMap = {};
|
|
36
|
+
|
|
37
|
+
// Match :paramName patterns (word characters after a colon)
|
|
38
|
+
// but not inside quoted strings and not ::type casts
|
|
39
|
+
var tmpText = pQueryBody.replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, function (pMatch, pParamName)
|
|
40
|
+
{
|
|
41
|
+
if (!pNamedParams.hasOwnProperty(pParamName))
|
|
42
|
+
{
|
|
43
|
+
// Not a known parameter; leave it as-is (could be a ::typecast)
|
|
44
|
+
return pMatch;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (tmpParamMap.hasOwnProperty(pParamName))
|
|
48
|
+
{
|
|
49
|
+
// Reuse the same positional index for duplicate references
|
|
50
|
+
return '$' + tmpParamMap[pParamName];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
tmpParamIndex++;
|
|
54
|
+
tmpParamMap[pParamName] = tmpParamIndex;
|
|
55
|
+
|
|
56
|
+
var tmpValue = pNamedParams[pParamName];
|
|
57
|
+
if (Array.isArray(tmpValue))
|
|
58
|
+
{
|
|
59
|
+
// IN clause: expand array into $N, $N+1, ...
|
|
60
|
+
var tmpPlaceholders = [];
|
|
61
|
+
for (var i = 0; i < tmpValue.length; i++)
|
|
62
|
+
{
|
|
63
|
+
if (i > 0)
|
|
64
|
+
{
|
|
65
|
+
tmpParamIndex++;
|
|
66
|
+
}
|
|
67
|
+
tmpValues.push(tmpValue[i]);
|
|
68
|
+
tmpPlaceholders.push('$' + tmpParamIndex);
|
|
69
|
+
}
|
|
70
|
+
return tmpPlaceholders.join(', ');
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
{
|
|
74
|
+
tmpValues.push(tmpValue);
|
|
75
|
+
return '$' + tmpParamIndex;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return { text: tmpText, values: tmpValues };
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Build a connection pool, shared within this provider.
|
|
84
|
+
*/
|
|
85
|
+
var getSQLPool = function ()
|
|
86
|
+
{
|
|
87
|
+
// New-style default connection pool provider
|
|
88
|
+
if (typeof (_Fable.MeadowPostgreSQLProvider) == 'object' && _Fable.MeadowPostgreSQLProvider.connected)
|
|
89
|
+
{
|
|
90
|
+
return _Fable.MeadowPostgreSQLProvider.pool;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
var getProvider = function ()
|
|
97
|
+
{
|
|
98
|
+
// New-style default connection pool provider
|
|
99
|
+
if (typeof (_Fable.MeadowPostgreSQLProvider) == 'object')
|
|
100
|
+
{
|
|
101
|
+
return _Fable.MeadowPostgreSQLProvider;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// The Meadow marshaller also passes in the Schema as the third parameter, but this is a blunt function ATM.
|
|
108
|
+
var marshalRecordFromSourceToObject = function (pObject, pRecord)
|
|
109
|
+
{
|
|
110
|
+
// For now, crudely assign everything in pRecord to pObject
|
|
111
|
+
for (var tmpColumn in pRecord)
|
|
112
|
+
{
|
|
113
|
+
pObject[tmpColumn] = pRecord[tmpColumn];
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
var Create = function (pQuery, fCallback)
|
|
118
|
+
{
|
|
119
|
+
var tmpResult = pQuery.parameters.result;
|
|
120
|
+
|
|
121
|
+
pQuery.setDialect('PostgreSQL').buildCreateQuery();
|
|
122
|
+
|
|
123
|
+
if (pQuery.logLevel > 0 ||
|
|
124
|
+
_GlobalLogLevel > 0)
|
|
125
|
+
{
|
|
126
|
+
_Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
var tmpConverted = convertNamedToPositional(pQuery.query.body, pQuery.query.parameters);
|
|
130
|
+
|
|
131
|
+
getSQLPool().query(
|
|
132
|
+
tmpConverted.text,
|
|
133
|
+
tmpConverted.values,
|
|
134
|
+
function (pError, pDBResult)
|
|
135
|
+
{
|
|
136
|
+
tmpResult.error = pError;
|
|
137
|
+
tmpResult.value = false;
|
|
138
|
+
try
|
|
139
|
+
{
|
|
140
|
+
// RETURNING * gives us the full row; find the AutoIdentity column
|
|
141
|
+
if (pDBResult && pDBResult.rows && pDBResult.rows.length > 0)
|
|
142
|
+
{
|
|
143
|
+
var tmpSchema = Array.isArray(pQuery.query.schema) ? pQuery.query.schema : [];
|
|
144
|
+
var tmpIDColumn = false;
|
|
145
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
146
|
+
{
|
|
147
|
+
if (tmpSchema[i].Type === 'AutoIdentity')
|
|
148
|
+
{
|
|
149
|
+
tmpIDColumn = tmpSchema[i].Column;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (tmpIDColumn && pDBResult.rows[0].hasOwnProperty(tmpIDColumn))
|
|
154
|
+
{
|
|
155
|
+
tmpResult.value = pDBResult.rows[0][tmpIDColumn];
|
|
156
|
+
}
|
|
157
|
+
else
|
|
158
|
+
{
|
|
159
|
+
// Fall back to the first column of the first row
|
|
160
|
+
var tmpFirstKey = Object.keys(pDBResult.rows[0])[0];
|
|
161
|
+
tmpResult.value = pDBResult.rows[0][tmpFirstKey];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (pErrorGettingID)
|
|
166
|
+
{
|
|
167
|
+
_Fable.log.warn('Error getting insert ID during create query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
tmpResult.executed = true;
|
|
171
|
+
return fCallback();
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// This is a synchronous read, good for a few records.
|
|
177
|
+
var Read = function (pQuery, fCallback)
|
|
178
|
+
{
|
|
179
|
+
var tmpResult = pQuery.parameters.result;
|
|
180
|
+
|
|
181
|
+
pQuery.setDialect('PostgreSQL').buildReadQuery();
|
|
182
|
+
|
|
183
|
+
if (pQuery.logLevel > 0 ||
|
|
184
|
+
_GlobalLogLevel > 0)
|
|
185
|
+
{
|
|
186
|
+
_Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
var tmpConverted = convertNamedToPositional(pQuery.query.body, pQuery.query.parameters);
|
|
190
|
+
|
|
191
|
+
getSQLPool().query(
|
|
192
|
+
tmpConverted.text,
|
|
193
|
+
tmpConverted.values,
|
|
194
|
+
function (pError, pDBResult)
|
|
195
|
+
{
|
|
196
|
+
tmpResult.error = pError;
|
|
197
|
+
tmpResult.value = pDBResult ? pDBResult.rows : [];
|
|
198
|
+
tmpResult.executed = true;
|
|
199
|
+
return fCallback();
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
var Update = function (pQuery, fCallback)
|
|
205
|
+
{
|
|
206
|
+
var tmpResult = pQuery.parameters.result;
|
|
207
|
+
|
|
208
|
+
pQuery.setDialect('PostgreSQL').buildUpdateQuery();
|
|
209
|
+
|
|
210
|
+
if (pQuery.logLevel > 0 ||
|
|
211
|
+
_GlobalLogLevel > 0)
|
|
212
|
+
{
|
|
213
|
+
_Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
var tmpConverted = convertNamedToPositional(pQuery.query.body, pQuery.query.parameters);
|
|
217
|
+
|
|
218
|
+
getSQLPool().query(
|
|
219
|
+
tmpConverted.text,
|
|
220
|
+
tmpConverted.values,
|
|
221
|
+
function (pError, pDBResult)
|
|
222
|
+
{
|
|
223
|
+
tmpResult.error = pError;
|
|
224
|
+
tmpResult.value = pDBResult ? pDBResult.rows : [];
|
|
225
|
+
tmpResult.executed = true;
|
|
226
|
+
return fCallback();
|
|
227
|
+
}
|
|
228
|
+
);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
var Delete = function (pQuery, fCallback)
|
|
232
|
+
{
|
|
233
|
+
var tmpResult = pQuery.parameters.result;
|
|
234
|
+
|
|
235
|
+
pQuery.setDialect('PostgreSQL').buildDeleteQuery();
|
|
236
|
+
|
|
237
|
+
if (pQuery.logLevel > 0 ||
|
|
238
|
+
_GlobalLogLevel > 0)
|
|
239
|
+
{
|
|
240
|
+
_Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
var tmpConverted = convertNamedToPositional(pQuery.query.body, pQuery.query.parameters);
|
|
244
|
+
|
|
245
|
+
getSQLPool().query(
|
|
246
|
+
tmpConverted.text,
|
|
247
|
+
tmpConverted.values,
|
|
248
|
+
function (pError, pDBResult)
|
|
249
|
+
{
|
|
250
|
+
tmpResult.error = pError;
|
|
251
|
+
tmpResult.value = false;
|
|
252
|
+
try
|
|
253
|
+
{
|
|
254
|
+
tmpResult.value = pDBResult ? pDBResult.rowCount : 0;
|
|
255
|
+
}
|
|
256
|
+
catch (pErrorGettingRowcount)
|
|
257
|
+
{
|
|
258
|
+
_Fable.log.warn('Error getting affected rowcount during delete query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
|
|
259
|
+
}
|
|
260
|
+
tmpResult.executed = true;
|
|
261
|
+
return fCallback();
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
var Undelete = function (pQuery, fCallback)
|
|
267
|
+
{
|
|
268
|
+
var tmpResult = pQuery.parameters.result;
|
|
269
|
+
|
|
270
|
+
pQuery.setDialect('PostgreSQL').buildUndeleteQuery();
|
|
271
|
+
|
|
272
|
+
if (pQuery.logLevel > 0 ||
|
|
273
|
+
_GlobalLogLevel > 0)
|
|
274
|
+
{
|
|
275
|
+
_Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
var tmpConverted = convertNamedToPositional(pQuery.query.body, pQuery.query.parameters);
|
|
279
|
+
|
|
280
|
+
getSQLPool().query(
|
|
281
|
+
tmpConverted.text,
|
|
282
|
+
tmpConverted.values,
|
|
283
|
+
function (pError, pDBResult)
|
|
284
|
+
{
|
|
285
|
+
tmpResult.error = pError;
|
|
286
|
+
tmpResult.value = false;
|
|
287
|
+
try
|
|
288
|
+
{
|
|
289
|
+
tmpResult.value = pDBResult ? pDBResult.rowCount : 0;
|
|
290
|
+
}
|
|
291
|
+
catch (pErrorGettingRowcount)
|
|
292
|
+
{
|
|
293
|
+
_Fable.log.warn('Error getting affected rowcount during undelete query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
|
|
294
|
+
}
|
|
295
|
+
tmpResult.executed = true;
|
|
296
|
+
return fCallback();
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
var Count = function (pQuery, fCallback)
|
|
302
|
+
{
|
|
303
|
+
var tmpResult = pQuery.parameters.result;
|
|
304
|
+
|
|
305
|
+
pQuery.setDialect('PostgreSQL').buildCountQuery();
|
|
306
|
+
|
|
307
|
+
if (pQuery.logLevel > 0 ||
|
|
308
|
+
_GlobalLogLevel > 0)
|
|
309
|
+
{
|
|
310
|
+
_Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
var tmpConverted = convertNamedToPositional(pQuery.query.body, pQuery.query.parameters);
|
|
314
|
+
|
|
315
|
+
getSQLPool().query(
|
|
316
|
+
tmpConverted.text,
|
|
317
|
+
tmpConverted.values,
|
|
318
|
+
function (pError, pDBResult)
|
|
319
|
+
{
|
|
320
|
+
tmpResult.executed = true;
|
|
321
|
+
tmpResult.error = pError;
|
|
322
|
+
tmpResult.value = false;
|
|
323
|
+
try
|
|
324
|
+
{
|
|
325
|
+
// PostgreSQL COUNT(*) returns bigint, which the pg library delivers as a string
|
|
326
|
+
tmpResult.value = parseInt(pDBResult.rows[0].rowcount, 10);
|
|
327
|
+
}
|
|
328
|
+
catch (pErrorGettingRowcount)
|
|
329
|
+
{
|
|
330
|
+
_Fable.log.warn('Error getting rowcount during count query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
|
|
331
|
+
}
|
|
332
|
+
return fCallback();
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
var tmpNewProvider = (
|
|
338
|
+
{
|
|
339
|
+
marshalRecordFromSourceToObject: marshalRecordFromSourceToObject,
|
|
340
|
+
|
|
341
|
+
Create: Create,
|
|
342
|
+
Read: Read,
|
|
343
|
+
Update: Update,
|
|
344
|
+
Delete: Delete,
|
|
345
|
+
Undelete: Undelete,
|
|
346
|
+
Count: Count,
|
|
347
|
+
|
|
348
|
+
getProvider: getProvider,
|
|
349
|
+
providerCreatesSupported: false,
|
|
350
|
+
|
|
351
|
+
new: createNew
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
return tmpNewProvider;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return createNew();
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
module.exports = new MeadowProvider();
|