foxhound 1.0.32 → 1.0.33
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/.vscode/launch.json +18 -0
- package/debug/Harness.js +46 -0
- package/package.json +1 -1
- package/source/dialects/ALASQL/FoxHound-Dialect-ALASQL.js +2 -2
- package/source/dialects/MeadowEndpoints/FoxHound-Dialect-MeadowEndpoints.js +445 -0
- package/source/dialects/MySQL/FoxHound-Dialect-MySQL.js +35 -9
- package/test/FoxHound-Dialect-ALASQL_tests.js +45 -3
- package/test/FoxHound-Dialect-MeadowEndpoints_tests.js +589 -0
- package/test/FoxHound-Dialect-MySQL_tests.js +21 -2
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "pwa-node",
|
|
9
|
+
"request": "launch",
|
|
10
|
+
"name": "Launch Program",
|
|
11
|
+
"outputCapture": "std",
|
|
12
|
+
"skipFiles": [
|
|
13
|
+
"<node_internals>/**"
|
|
14
|
+
],
|
|
15
|
+
"program": "${workspaceFolder}/debug/Harness.js"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/debug/Harness.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
var libFable = require('fable').new({});
|
|
2
|
+
var libFoxHound = require('../source/FoxHound.js');
|
|
3
|
+
|
|
4
|
+
var _AnimalSchema = (
|
|
5
|
+
[
|
|
6
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
7
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
8
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
9
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
10
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
11
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" },
|
|
12
|
+
{ Column: "Deleted", Type:"Deleted" },
|
|
13
|
+
{ Column: "DeletingIDUser", Type:"DeleteIDUser" },
|
|
14
|
+
{ Column: "DeleteDate", Type:"DeleteDate" }
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
var _AnimalSchemaWithoutDeleted = (
|
|
18
|
+
[
|
|
19
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
20
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
21
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
22
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
23
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
24
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" }
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
28
|
+
.setDialect('MeadowEndpoints')
|
|
29
|
+
.setScope('Animal')
|
|
30
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
31
|
+
.setCap(100)
|
|
32
|
+
.addFilter('Age', '25')
|
|
33
|
+
.addFilter('', '', '(')
|
|
34
|
+
.addFilter('Color', 'Red')
|
|
35
|
+
.addFilter('Color', 'Green', '=', 'OR')
|
|
36
|
+
.addFilter('', '', ')')
|
|
37
|
+
.addFilter('Description', '', 'IS NOT NULL')
|
|
38
|
+
.addFilter('IDOffice', [10, 11, 15, 18, 22], 'IN');
|
|
39
|
+
tmpQuery.setLogLevel(3).addSort('Age');
|
|
40
|
+
// Build the query
|
|
41
|
+
tmpQuery.buildReadQuery();
|
|
42
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
43
|
+
libFable.log.trace('Select Query', tmpQuery.query);
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
console.log(tmpQuery.query.body);
|
package/package.json
CHANGED
|
@@ -739,9 +739,9 @@ var FoxHoundDialectALASQL = function()
|
|
|
739
739
|
var Count = function(pParameters)
|
|
740
740
|
{
|
|
741
741
|
var tmpTableName = generateTableName(pParameters);
|
|
742
|
-
var tmpFieldList = generateFieldList(pParameters);
|
|
743
742
|
var tmpWhere = generateWhere(pParameters);
|
|
744
743
|
const tmpOptDistinct = pParameters.distinct ? 'DISTINCT' : '';
|
|
744
|
+
const tmpCountClause = pParameters.distinct ? `${tmpOptDistinct}${generateFieldList(pParameters)}` : '*';
|
|
745
745
|
|
|
746
746
|
if (pParameters.queryOverride)
|
|
747
747
|
{
|
|
@@ -758,7 +758,7 @@ var FoxHoundDialectALASQL = function()
|
|
|
758
758
|
}
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
-
return `SELECT COUNT(${
|
|
761
|
+
return `SELECT COUNT(${tmpCountClause}) AS RowCount FROM${tmpTableName}${tmpWhere};`;
|
|
762
762
|
};
|
|
763
763
|
|
|
764
764
|
var tmpDialect = ({
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FoxHound Meadow Endpoints Dialect
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
* @class FoxHoundDialectMeadowEndpoints
|
|
8
|
+
*/
|
|
9
|
+
var libUnderscore = require('underscore');
|
|
10
|
+
|
|
11
|
+
var FoxHoundDialectMeadowEndpoints = function()
|
|
12
|
+
{
|
|
13
|
+
/**
|
|
14
|
+
* Generate a table name from the scope
|
|
15
|
+
*
|
|
16
|
+
* @method: generateTableName
|
|
17
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
18
|
+
* @return: {String} Returns the table name clause
|
|
19
|
+
*/
|
|
20
|
+
var generateTableName = function(pParameters)
|
|
21
|
+
{
|
|
22
|
+
return pParameters.scope;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generate a field list from the array of dataElements
|
|
27
|
+
*
|
|
28
|
+
* Each entry in the dataElements is a simple string
|
|
29
|
+
*
|
|
30
|
+
* @method: generateFieldList
|
|
31
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
32
|
+
* @return: {String} Returns the field list clause
|
|
33
|
+
*/
|
|
34
|
+
var generateFieldList = function(pParameters)
|
|
35
|
+
{
|
|
36
|
+
var tmpDataElements = pParameters.dataElements;
|
|
37
|
+
if (!Array.isArray(tmpDataElements) || tmpDataElements.length < 1)
|
|
38
|
+
{
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
var tmpFieldList = '';
|
|
43
|
+
for (var i = 0; i < tmpDataElements.length; i++)
|
|
44
|
+
{
|
|
45
|
+
if (i > 0)
|
|
46
|
+
{
|
|
47
|
+
tmpFieldList += ',';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
tmpFieldList += tmpDataElements[i];
|
|
51
|
+
}
|
|
52
|
+
return tmpFieldList;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generate a query from the array of where clauses
|
|
57
|
+
*
|
|
58
|
+
* Each clause is an object like:
|
|
59
|
+
{
|
|
60
|
+
Column:'Name',
|
|
61
|
+
Operator:'EQ',
|
|
62
|
+
Value:'John',
|
|
63
|
+
Connector:'And',
|
|
64
|
+
Parameter:'Name'
|
|
65
|
+
}
|
|
66
|
+
*
|
|
67
|
+
* @method: generateWhere
|
|
68
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
69
|
+
* @return: {String} Returns the WHERE clause prefixed with WHERE, or an empty string if unnecessary
|
|
70
|
+
*/
|
|
71
|
+
var generateWhere = function(pParameters)
|
|
72
|
+
{
|
|
73
|
+
var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : [];
|
|
74
|
+
var tmpTableName = generateTableName(pParameters);
|
|
75
|
+
|
|
76
|
+
var tmpURL = '';
|
|
77
|
+
|
|
78
|
+
let tmpfAddFilter = (pFilterCommand, pFilterParameters) =>
|
|
79
|
+
{
|
|
80
|
+
if (tmpURL.length > 0)
|
|
81
|
+
{
|
|
82
|
+
tmpURL += '~';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
tmpURL += `${pFilterCommand}~${pFilterParameters[0]}~${pFilterParameters[1]}~${pFilterParameters[2]}`;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
let tmpfTranslateOperator = (pOperator) =>
|
|
89
|
+
{
|
|
90
|
+
tmpNewOperator = 'EQ';
|
|
91
|
+
switch(pOperator.toUpperCase())
|
|
92
|
+
{
|
|
93
|
+
case '!=':
|
|
94
|
+
tmpNewOperator = 'NE';
|
|
95
|
+
break;
|
|
96
|
+
case '>':
|
|
97
|
+
tmpNewOperator = 'GT';
|
|
98
|
+
break;
|
|
99
|
+
case '>=':
|
|
100
|
+
tmpNewOperator = 'GE';
|
|
101
|
+
break;
|
|
102
|
+
case '<=':
|
|
103
|
+
tmpNewOperator = 'LE';
|
|
104
|
+
break;
|
|
105
|
+
case '<':
|
|
106
|
+
tmpNewOperator = 'LT';
|
|
107
|
+
break;
|
|
108
|
+
case 'LIKE':
|
|
109
|
+
tmpNewOperator = 'LK';
|
|
110
|
+
break;
|
|
111
|
+
case 'IN':
|
|
112
|
+
tmpNewOperator = 'INN';
|
|
113
|
+
break;
|
|
114
|
+
case 'NOT IN':
|
|
115
|
+
tmpNewOperator = 'NI';
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
return tmpNewOperator;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Translating Delete Tracking bit on query to a query with automagic
|
|
122
|
+
// This will eventually deprecate this as part of the necessary query
|
|
123
|
+
if (pParameters.query.disableDeleteTracking)
|
|
124
|
+
{
|
|
125
|
+
tmpfAddFilter('FBV',['Deleted','GE','0'])
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (var i = 0; i < tmpFilter.length; i++)
|
|
129
|
+
{
|
|
130
|
+
if (tmpFilter[i].Operator === '(')
|
|
131
|
+
{
|
|
132
|
+
tmpfAddFilter('FOP',['0','(','0']);
|
|
133
|
+
}
|
|
134
|
+
else if (tmpFilter[i].Operator === ')')
|
|
135
|
+
{
|
|
136
|
+
// Close a logical grouping
|
|
137
|
+
tmpfAddFilter('FCP',['0',')','0']);
|
|
138
|
+
}
|
|
139
|
+
else if (tmpFilter[i].Operator === 'IN' || tmpFilter[i].Operator === "NOT IN")
|
|
140
|
+
{
|
|
141
|
+
let tmpFilterCommand = 'FBV';
|
|
142
|
+
if (tmpFilter[i].Connector == 'OR')
|
|
143
|
+
{
|
|
144
|
+
tmpFilterCommand = 'FBVOR';
|
|
145
|
+
}
|
|
146
|
+
// Add the column name, operator and parameter name to the list of where value parenthetical
|
|
147
|
+
tmpfAddFilter(tmpFilterCommand, [tmpFilter[i].Column, tmpfTranslateOperator(tmpFilter[i].Operator), tmpFilter[i].Value.map(encodeURIComponent).join(',')])
|
|
148
|
+
}
|
|
149
|
+
else if (tmpFilter[i].Operator === 'IS NULL')
|
|
150
|
+
{
|
|
151
|
+
// IS NULL is a special operator which doesn't require a value, or parameter
|
|
152
|
+
tmpfAddFilter('FBV', [tmpFilter[i].Column, 'IN', '0']);
|
|
153
|
+
}
|
|
154
|
+
else if (tmpFilter[i].Operator === 'IS NOT NULL')
|
|
155
|
+
{
|
|
156
|
+
// IS NOT NULL is a special operator which doesn't require a value, or parameter
|
|
157
|
+
tmpfAddFilter('FBV', [tmpFilter[i].Column, 'NN', '0']);
|
|
158
|
+
}
|
|
159
|
+
else
|
|
160
|
+
{
|
|
161
|
+
let tmpFilterCommand = 'FBV';
|
|
162
|
+
if (tmpFilter[i].Connector == 'OR')
|
|
163
|
+
{
|
|
164
|
+
tmpFilterCommand = 'FBVOR';
|
|
165
|
+
}
|
|
166
|
+
// Add the column name, operator and parameter name to the list of where value parenthetical
|
|
167
|
+
tmpfAddFilter(tmpFilterCommand, [tmpFilter[i].Column, tmpfTranslateOperator(tmpFilter[i].Operator), encodeURIComponent(tmpFilter[i].Value)]);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let tmpOrderBy = generateOrderBy(pParameters);
|
|
172
|
+
if (tmpOrderBy)
|
|
173
|
+
{
|
|
174
|
+
if (tmpURL)
|
|
175
|
+
{
|
|
176
|
+
tmpURL += '~';
|
|
177
|
+
}
|
|
178
|
+
tmpURL += tmpOrderBy;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return tmpURL;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get the flags for the request
|
|
186
|
+
*
|
|
187
|
+
* These are usually passed in for Update and Create when extra tracking is disabled.
|
|
188
|
+
*
|
|
189
|
+
* @method: generateFlags
|
|
190
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
191
|
+
* @return: {String} Flags to be sent, if any.
|
|
192
|
+
*/
|
|
193
|
+
function generateFlags(pParameters)
|
|
194
|
+
{
|
|
195
|
+
let tmpDisableAutoDateStamp = pParameters.query.disableAutoDateStamp;
|
|
196
|
+
let tmpDisableDeleteTracking = pParameters.query.disableDeleteTracking;
|
|
197
|
+
let tmpDisableAutoIdentity = pParameters.query.disableAutoIdentity;
|
|
198
|
+
let tmpDisableAutoUserStamp = pParameters.query.disableAutoUserStamp;
|
|
199
|
+
|
|
200
|
+
let tmpFlags = '';
|
|
201
|
+
|
|
202
|
+
let fAddFlag = (pFlagSet, pFlag) =>
|
|
203
|
+
{
|
|
204
|
+
if (pFlagSet)
|
|
205
|
+
{
|
|
206
|
+
if (tmpFlags.length > 0)
|
|
207
|
+
{
|
|
208
|
+
tmpFlags += ',';
|
|
209
|
+
}
|
|
210
|
+
tmpFlags += pFlag;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
fAddFlag(tmpDisableAutoDateStamp, 'DisableAutoDateStamp');
|
|
215
|
+
fAddFlag(tmpDisableDeleteTracking, 'DisableDeleteTracking');
|
|
216
|
+
fAddFlag(tmpDisableAutoIdentity, 'DisableAutoIdentity');
|
|
217
|
+
fAddFlag(tmpDisableAutoUserStamp, 'DisableAutoUserStamp');
|
|
218
|
+
|
|
219
|
+
return tmpFlags;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get the ID for the record, to be used in URIs
|
|
224
|
+
*
|
|
225
|
+
* @method: getIDRecord
|
|
226
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
227
|
+
* @return: {String} ID of the record in string form for the URI
|
|
228
|
+
*/
|
|
229
|
+
var getIDRecord = function(pParameters)
|
|
230
|
+
{
|
|
231
|
+
var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : [];
|
|
232
|
+
|
|
233
|
+
var tmpIDRecord = false;
|
|
234
|
+
|
|
235
|
+
if (tmpFilter.length < 1)
|
|
236
|
+
{
|
|
237
|
+
return tmpIDRecord;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (var i = 0; i < tmpFilter.length; i++)
|
|
241
|
+
{
|
|
242
|
+
// Check Schema Entry Type
|
|
243
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
244
|
+
var tmpSchemaEntry = {Column:tmpFilter[i].Column, Type:'Default'};
|
|
245
|
+
for (var j = 0; j < tmpSchema.length; j++)
|
|
246
|
+
{
|
|
247
|
+
// If this column is the AutoIdentity, set it.
|
|
248
|
+
if ((tmpFilter[i].Column == tmpSchema[j].Column) &&
|
|
249
|
+
(tmpSchema[j].Type == 'AutoIdentity'))
|
|
250
|
+
{
|
|
251
|
+
tmpIDRecord = tmpFilter[i].Value;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return tmpIDRecord;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Generate an ORDER BY clause from the sort array
|
|
262
|
+
*
|
|
263
|
+
* Each entry in the sort is an object like:
|
|
264
|
+
* {Column:'Color',Direction:'Descending'}
|
|
265
|
+
*
|
|
266
|
+
* @method: generateOrderBy
|
|
267
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
268
|
+
* @return: {String} Returns the field list clause
|
|
269
|
+
*/
|
|
270
|
+
var generateOrderBy = function(pParameters)
|
|
271
|
+
{
|
|
272
|
+
var tmpOrderBy = pParameters.sort;
|
|
273
|
+
var tmpOrderClause = false;
|
|
274
|
+
|
|
275
|
+
if (!Array.isArray(tmpOrderBy) || tmpOrderBy.length < 1)
|
|
276
|
+
{
|
|
277
|
+
return tmpOrderClause;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
tmpOrderClause = '';
|
|
281
|
+
|
|
282
|
+
for (var i = 0; i < tmpOrderBy.length; i++)
|
|
283
|
+
{
|
|
284
|
+
if (i > 0)
|
|
285
|
+
{
|
|
286
|
+
tmpOrderClause += '~';
|
|
287
|
+
}
|
|
288
|
+
tmpOrderClause += `FSF~${tmpOrderBy[i].Column}~`;
|
|
289
|
+
|
|
290
|
+
if (tmpOrderBy[i].Direction == 'Descending')
|
|
291
|
+
{
|
|
292
|
+
tmpOrderClause += 'DESC~0';
|
|
293
|
+
}
|
|
294
|
+
else
|
|
295
|
+
{
|
|
296
|
+
tmpOrderClause += 'ASC~0'
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return tmpOrderClause;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Generate the limit clause
|
|
304
|
+
*
|
|
305
|
+
* @method: generateLimit
|
|
306
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
307
|
+
* @return: {String} Returns the table name clause
|
|
308
|
+
*/
|
|
309
|
+
var generateLimit = function(pParameters)
|
|
310
|
+
{
|
|
311
|
+
if (!pParameters.cap)
|
|
312
|
+
{
|
|
313
|
+
return '';
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let tmpBegin = (pParameters.begin !== false) ? pParameters.begin : 0;
|
|
317
|
+
|
|
318
|
+
return `${tmpBegin}/${pParameters.cap}`;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
var Create = function(pParameters)
|
|
322
|
+
{
|
|
323
|
+
var tmpTableName = generateTableName(pParameters);
|
|
324
|
+
var tmpFlags = generateFlags(pParameters);
|
|
325
|
+
|
|
326
|
+
if (tmpTableName)
|
|
327
|
+
{
|
|
328
|
+
let tmpURL = tmpTableName;
|
|
329
|
+
if (tmpFlags)
|
|
330
|
+
{
|
|
331
|
+
tmpURL = `${tmpURL}/WithFlags/${tmpFlags}`
|
|
332
|
+
}
|
|
333
|
+
return tmpURL;
|
|
334
|
+
}
|
|
335
|
+
else
|
|
336
|
+
{
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Read one or many records
|
|
344
|
+
*
|
|
345
|
+
* @method Read
|
|
346
|
+
* @param {Object} pParameters SQL Query parameters
|
|
347
|
+
* @return {String} Returns the current Query for chaining.
|
|
348
|
+
*/
|
|
349
|
+
var Read = function(pParameters)
|
|
350
|
+
{
|
|
351
|
+
var tmpTableName = generateTableName(pParameters);
|
|
352
|
+
var tmpFieldList = generateFieldList(pParameters);
|
|
353
|
+
var tmpWhere = generateWhere(pParameters);
|
|
354
|
+
var tmpLimit = generateLimit(pParameters);
|
|
355
|
+
|
|
356
|
+
var tmpURL = `${tmpTableName}s`;
|
|
357
|
+
if (tmpFieldList)
|
|
358
|
+
{
|
|
359
|
+
tmpURL = `${tmpURL}/LiteExtended/${tmpFieldList}`
|
|
360
|
+
}
|
|
361
|
+
if (tmpWhere)
|
|
362
|
+
{
|
|
363
|
+
tmpURL = `${tmpURL}/FilteredTo/${tmpWhere}`;
|
|
364
|
+
}
|
|
365
|
+
if (tmpLimit)
|
|
366
|
+
{
|
|
367
|
+
tmpURL = `${tmpURL}/${tmpLimit}`;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return tmpURL;
|
|
371
|
+
///'SELECT'+tmpFieldList+' FROM'+tmpTableName+tmpJoin+tmpWhere+tmpOrderBy+tmpLimit+';';
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
var Update = function(pParameters)
|
|
375
|
+
{
|
|
376
|
+
var tmpTableName = generateTableName(pParameters);
|
|
377
|
+
var tmpFlags = generateFlags(pParameters);
|
|
378
|
+
|
|
379
|
+
if (tmpTableName)
|
|
380
|
+
{
|
|
381
|
+
let tmpURL = tmpTableName;
|
|
382
|
+
if (tmpFlags)
|
|
383
|
+
{
|
|
384
|
+
tmpURL = `${tmpURL}/WithFlags/${tmpFlags}`
|
|
385
|
+
}
|
|
386
|
+
return tmpURL;
|
|
387
|
+
}
|
|
388
|
+
else
|
|
389
|
+
{
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
var Delete = function(pParameters)
|
|
395
|
+
{
|
|
396
|
+
var tmpTableName = generateTableName(pParameters);
|
|
397
|
+
var tmpIDRecord = getIDRecord(pParameters);
|
|
398
|
+
|
|
399
|
+
if (!tmpIDRecord)
|
|
400
|
+
{
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return `${tmpTableName}/${tmpIDRecord}`;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
var Count = function(pParameters)
|
|
408
|
+
{
|
|
409
|
+
var tmpTableName = generateTableName(pParameters);
|
|
410
|
+
var tmpWhere = generateWhere(pParameters);
|
|
411
|
+
|
|
412
|
+
let tmpCountQuery = `${tmpTableName}s/Count`;
|
|
413
|
+
|
|
414
|
+
if (tmpWhere)
|
|
415
|
+
{
|
|
416
|
+
return `${tmpCountQuery}s/Count/FilteredTo/${tmpWhere}`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return tmpCountQuery;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
var tmpDialect = ({
|
|
423
|
+
Create: Create,
|
|
424
|
+
Read: Read,
|
|
425
|
+
Update: Update,
|
|
426
|
+
Delete: Delete,
|
|
427
|
+
Count: Count
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Dialect Name
|
|
432
|
+
*
|
|
433
|
+
* @property name
|
|
434
|
+
* @type string
|
|
435
|
+
*/
|
|
436
|
+
Object.defineProperty(tmpDialect, 'name',
|
|
437
|
+
{
|
|
438
|
+
get: function() { return 'MeadowEndpoints'; },
|
|
439
|
+
enumerable: true
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
return tmpDialect;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
module.exports = new FoxHoundDialectMeadowEndpoints();
|
|
@@ -44,14 +44,28 @@ var FoxHoundDialectMySQL = function()
|
|
|
44
44
|
*
|
|
45
45
|
* @method: generateFieldList
|
|
46
46
|
* @param: {Object} pParameters SQL Query Parameters
|
|
47
|
-
* @
|
|
47
|
+
* @param {Boolean} pExplicitFields (optional) If true, generate explicit fields rather than "TableName.*" (for count distinct).
|
|
48
|
+
* @return: {String} Returns the field list clause, or empty string if explicit fields are requested but cannot be fulfilled
|
|
49
|
+
* due to missing schema.
|
|
48
50
|
*/
|
|
49
|
-
var generateFieldList = function(pParameters)
|
|
51
|
+
var generateFieldList = function(pParameters, pExplicitFields)
|
|
50
52
|
{
|
|
51
53
|
var tmpDataElements = pParameters.dataElements;
|
|
52
54
|
if (!Array.isArray(tmpDataElements) || tmpDataElements.length < 1)
|
|
53
55
|
{
|
|
54
|
-
|
|
56
|
+
const tmpTableName = generateTableName(pParameters);
|
|
57
|
+
if (!pExplicitFields)
|
|
58
|
+
{
|
|
59
|
+
return tmpTableName + '.*';
|
|
60
|
+
}
|
|
61
|
+
// we need to list all of the table fields explicitly; get them from the schema
|
|
62
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
63
|
+
tmpDataElements = tmpSchema.map((entry) => `${tmpTableName}.${entry.Column}`.trim());
|
|
64
|
+
if (tmpDataElements.length < 1)
|
|
65
|
+
{
|
|
66
|
+
// this means we have no schema; returning an empty string here signals the calling code to handle this case
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
55
69
|
}
|
|
56
70
|
|
|
57
71
|
var tmpFieldList = ' ';
|
|
@@ -77,19 +91,26 @@ var FoxHoundDialectMySQL = function()
|
|
|
77
91
|
return tmpFieldList;
|
|
78
92
|
};
|
|
79
93
|
|
|
94
|
+
const SURROUNDING_QUOTES_AND_WHITESPACE_REGEX = /^[` ]+|[` ]+$/g;
|
|
95
|
+
|
|
96
|
+
const cleanseQuoting = (str) =>
|
|
97
|
+
{
|
|
98
|
+
return str.replace(SURROUNDING_QUOTES_AND_WHITESPACE_REGEX, '');
|
|
99
|
+
};
|
|
100
|
+
|
|
80
101
|
/**
|
|
81
102
|
* Ensure a field name is properly escaped.
|
|
82
103
|
*/
|
|
83
104
|
var generateSafeFieldName = function(pFieldName)
|
|
84
105
|
{
|
|
85
|
-
pFieldNames = pFieldName.
|
|
106
|
+
let pFieldNames = pFieldName.split('.');
|
|
86
107
|
if (pFieldNames.length > 1)
|
|
87
108
|
{
|
|
88
|
-
return "`" + pFieldNames[0] + "`.`" + pFieldNames[1] + "`";
|
|
109
|
+
return "`" + cleanseQuoting(pFieldNames[0]) + "`.`" + cleanseQuoting(pFieldNames[1]) + "`";
|
|
89
110
|
}
|
|
90
111
|
else
|
|
91
112
|
{
|
|
92
|
-
return "`" + pFieldNames[0] + "`";
|
|
113
|
+
return "`" + cleanseQuoting(pFieldNames[0]) + "`";
|
|
93
114
|
}
|
|
94
115
|
}
|
|
95
116
|
|
|
@@ -776,11 +797,16 @@ var FoxHoundDialectMySQL = function()
|
|
|
776
797
|
|
|
777
798
|
var Count = function(pParameters)
|
|
778
799
|
{
|
|
779
|
-
var tmpFieldList = generateFieldList(pParameters);
|
|
800
|
+
var tmpFieldList = pParameters.distinct ? generateFieldList(pParameters, true) : '*';
|
|
780
801
|
var tmpTableName = generateTableName(pParameters);
|
|
781
802
|
var tmpJoin = generateJoins(pParameters);
|
|
782
803
|
var tmpWhere = generateWhere(pParameters);
|
|
783
|
-
|
|
804
|
+
// here, we ignore the distinct keyword if no fields have bene specified and
|
|
805
|
+
if (pParameters.distinct && tmpFieldList.length < 1)
|
|
806
|
+
{
|
|
807
|
+
console.warn('Distinct requested but no field list or schema are available, so not honoring distinct for count query.');
|
|
808
|
+
}
|
|
809
|
+
const tmpOptDistinct = pParameters.distinct && tmpFieldList.length > 0 ? 'DISTINCT' : '';
|
|
784
810
|
|
|
785
811
|
if (pParameters.queryOverride)
|
|
786
812
|
{
|
|
@@ -797,7 +823,7 @@ var FoxHoundDialectMySQL = function()
|
|
|
797
823
|
}
|
|
798
824
|
}
|
|
799
825
|
|
|
800
|
-
return `SELECT COUNT(${tmpOptDistinct}${tmpFieldList}) AS RowCount FROM${tmpTableName}${tmpJoin}${tmpWhere};`;
|
|
826
|
+
return `SELECT COUNT(${tmpOptDistinct}${tmpFieldList || '*'}) AS RowCount FROM${tmpTableName}${tmpJoin}${tmpWhere};`;
|
|
801
827
|
};
|
|
802
828
|
|
|
803
829
|
var tmpDialect = ({
|
|
@@ -290,6 +290,49 @@ suite
|
|
|
290
290
|
}
|
|
291
291
|
);
|
|
292
292
|
test
|
|
293
|
+
(
|
|
294
|
+
'Count Query with Field Overrides',
|
|
295
|
+
function()
|
|
296
|
+
{
|
|
297
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
298
|
+
.setDialect('ALASQL')
|
|
299
|
+
.setScope('Animal')
|
|
300
|
+
.setCap(10)
|
|
301
|
+
.setBegin(0)
|
|
302
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
303
|
+
.setSort([{Column:'Age',Direction:'Ascending'},{Column:'Cost',Direction:'Descending'}])
|
|
304
|
+
.setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'});
|
|
305
|
+
// Build the query
|
|
306
|
+
tmpQuery.buildCountQuery();
|
|
307
|
+
// This is the query generated by the ALASQL dialect
|
|
308
|
+
libFable.log.trace('Custom Select Query', tmpQuery.query);
|
|
309
|
+
Expect(tmpQuery.query.body)
|
|
310
|
+
.to.equal('SELECT COUNT(*) AS RowCount FROM Animal WHERE `Age` = :Age_w0;');
|
|
311
|
+
}
|
|
312
|
+
);
|
|
313
|
+
test
|
|
314
|
+
(
|
|
315
|
+
'Count Query with Field Overrides and Distinct',
|
|
316
|
+
function()
|
|
317
|
+
{
|
|
318
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
319
|
+
.setDialect('ALASQL')
|
|
320
|
+
.setScope('Animal')
|
|
321
|
+
.setCap(10)
|
|
322
|
+
.setBegin(0)
|
|
323
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
324
|
+
.setSort([{Column:'Age',Direction:'Ascending'},{Column:'Cost',Direction:'Descending'}])
|
|
325
|
+
.setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'})
|
|
326
|
+
.setDistinct(true);
|
|
327
|
+
// Build the query
|
|
328
|
+
tmpQuery.buildCountQuery();
|
|
329
|
+
// This is the query generated by the ALASQL dialect
|
|
330
|
+
libFable.log.trace('Custom Select Query', tmpQuery.query);
|
|
331
|
+
Expect(tmpQuery.query.body)
|
|
332
|
+
.to.equal('SELECT COUNT(DISTINCT `Name`, `Age`, `Cost`) AS RowCount FROM Animal WHERE `Age` = :Age_w0;');
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
test
|
|
293
336
|
(
|
|
294
337
|
'Custom Count Query with Distinct',
|
|
295
338
|
function()
|
|
@@ -299,13 +342,12 @@ suite
|
|
|
299
342
|
.setScope('Animal')
|
|
300
343
|
.setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'})
|
|
301
344
|
.setDistinct(true);
|
|
302
|
-
tmpQuery.parameters.queryOverride = 'SELECT COUNT(<%= Distinct %> *) AS RowCount FROM <%= TableName %> <%= Where %>;';
|
|
303
345
|
// Build the query
|
|
304
346
|
tmpQuery.buildCountQuery();
|
|
305
347
|
// This is the query generated by the ALASQL dialect
|
|
306
348
|
libFable.log.trace('Custom Count Query', tmpQuery.query);
|
|
307
349
|
Expect(tmpQuery.query.body)
|
|
308
|
-
.to.equal('SELECT COUNT(DISTINCT *) AS RowCount FROM
|
|
350
|
+
.to.equal('SELECT COUNT(DISTINCT *) AS RowCount FROM Animal WHERE `Age` = :Age_w0;');
|
|
309
351
|
}
|
|
310
352
|
);
|
|
311
353
|
test
|
|
@@ -375,7 +417,7 @@ suite
|
|
|
375
417
|
// This is the query generated by the ALASQL dialect
|
|
376
418
|
libFable.log.trace('Count Query', tmpQuery.query);
|
|
377
419
|
Expect(tmpQuery.query.body)
|
|
378
|
-
.to.equal('SELECT COUNT(
|
|
420
|
+
.to.equal('SELECT COUNT(*) AS RowCount FROM Animal;');
|
|
379
421
|
}
|
|
380
422
|
);
|
|
381
423
|
}
|
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for FoxHound
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var Chai = require('chai');
|
|
10
|
+
var Expect = Chai.expect;
|
|
11
|
+
var Assert = Chai.assert;
|
|
12
|
+
|
|
13
|
+
var libFable = require('fable').new({});
|
|
14
|
+
var libFoxHound = require('../source/FoxHound.js');
|
|
15
|
+
|
|
16
|
+
var _AnimalSchema = (
|
|
17
|
+
[
|
|
18
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
19
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
20
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
21
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
22
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
23
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" },
|
|
24
|
+
{ Column: "Deleted", Type:"Deleted" },
|
|
25
|
+
{ Column: "DeletingIDUser", Type:"DeleteIDUser" },
|
|
26
|
+
{ Column: "DeleteDate", Type:"DeleteDate" }
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
var _AnimalSchemaWithoutDeleted = (
|
|
30
|
+
[
|
|
31
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
32
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
33
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
34
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
35
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
36
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" }
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
suite
|
|
40
|
+
(
|
|
41
|
+
'FoxHound-Dialect-MeadowEndpoints',
|
|
42
|
+
function()
|
|
43
|
+
{
|
|
44
|
+
setup
|
|
45
|
+
(
|
|
46
|
+
function()
|
|
47
|
+
{
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
suite
|
|
52
|
+
(
|
|
53
|
+
'Object Sanity',
|
|
54
|
+
function()
|
|
55
|
+
{
|
|
56
|
+
test
|
|
57
|
+
(
|
|
58
|
+
'initialize should build a happy little object',
|
|
59
|
+
function()
|
|
60
|
+
{
|
|
61
|
+
var testFoxHound = libFoxHound.new(libFable).setDialect('MeadowEndpoints');
|
|
62
|
+
Expect(testFoxHound.dialect.name)
|
|
63
|
+
.to.equal('MeadowEndpoints');
|
|
64
|
+
Expect(testFoxHound)
|
|
65
|
+
.to.be.an('object', 'FoxHound with MeadowEndpoints should initialize as an object directly from the require statement.');
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
suite
|
|
72
|
+
(
|
|
73
|
+
'Basic Query Generation',
|
|
74
|
+
function()
|
|
75
|
+
{
|
|
76
|
+
test
|
|
77
|
+
(
|
|
78
|
+
'Create Query',
|
|
79
|
+
function()
|
|
80
|
+
{
|
|
81
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
82
|
+
.setLogLevel(5)
|
|
83
|
+
.setDialect('MeadowEndpoints')
|
|
84
|
+
.setScope('Animal')
|
|
85
|
+
.addRecord({IDAnimal:null, Name:'Foo Foo', Age:15});
|
|
86
|
+
// Build the query
|
|
87
|
+
tmpQuery.buildCreateQuery();
|
|
88
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
89
|
+
libFable.log.trace('Create Query', tmpQuery.query);
|
|
90
|
+
Expect(tmpQuery.query.body)
|
|
91
|
+
.to.equal("Animal");
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
test
|
|
95
|
+
(
|
|
96
|
+
'Bad Create Query',
|
|
97
|
+
function()
|
|
98
|
+
{
|
|
99
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints');
|
|
100
|
+
// Build the query
|
|
101
|
+
tmpQuery.buildCreateQuery();
|
|
102
|
+
tmpQuery.addRecord({});
|
|
103
|
+
tmpQuery.buildCreateQuery();
|
|
104
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
105
|
+
libFable.log.trace('Create Query', tmpQuery.query);
|
|
106
|
+
Expect(tmpQuery.query.body)
|
|
107
|
+
.to.equal(false);
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
test
|
|
111
|
+
(
|
|
112
|
+
'Read Query',
|
|
113
|
+
function()
|
|
114
|
+
{
|
|
115
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints').setScope('Animal');
|
|
116
|
+
tmpQuery.addSort({Column:'Cost',Direction:'Descending'});
|
|
117
|
+
// Build the query
|
|
118
|
+
tmpQuery.buildReadQuery();
|
|
119
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
120
|
+
libFable.log.trace('Simple Select Query', tmpQuery.query);
|
|
121
|
+
Expect(tmpQuery.query.body)
|
|
122
|
+
.to.equal('Animals/FilteredTo/FSF~Cost~DESC~0');
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
test
|
|
126
|
+
(
|
|
127
|
+
'Complex Read Query',
|
|
128
|
+
function()
|
|
129
|
+
{
|
|
130
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
131
|
+
.setDialect('MeadowEndpoints')
|
|
132
|
+
.setScope('Animal')
|
|
133
|
+
.setCap(10)
|
|
134
|
+
.setBegin(0)
|
|
135
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
136
|
+
.setSort([{Column:'Age',Direction:'Ascending'}])
|
|
137
|
+
.setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'});
|
|
138
|
+
tmpQuery.addSort('Cost');
|
|
139
|
+
// Build the query
|
|
140
|
+
tmpQuery.buildReadQuery();
|
|
141
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
142
|
+
libFable.log.trace('Select Query', tmpQuery.query);
|
|
143
|
+
Expect(tmpQuery.query.body)
|
|
144
|
+
.to.equal('Animals/LiteExtended/Name,Age,Cost/FilteredTo/FBV~Age~EQ~15~FSF~Age~ASC~0~FSF~Cost~ASC~0/0/10')
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
test
|
|
148
|
+
(
|
|
149
|
+
'Complex Read Query 2',
|
|
150
|
+
function()
|
|
151
|
+
{
|
|
152
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
153
|
+
.setDialect('MeadowEndpoints')
|
|
154
|
+
.setScope('Animal')
|
|
155
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
156
|
+
.setCap(100)
|
|
157
|
+
.addFilter('Age', '25')
|
|
158
|
+
.addFilter('', '', '(')
|
|
159
|
+
.addFilter('Color', 'Red')
|
|
160
|
+
.addFilter('Color', 'Green', '=', 'OR')
|
|
161
|
+
.addFilter('', '', ')')
|
|
162
|
+
.addFilter('Description', '', 'IS NOT NULL')
|
|
163
|
+
.addFilter('IDOffice', [10, 11, 15, 18, 22], 'IN');
|
|
164
|
+
tmpQuery.setLogLevel(3).addSort('Age');
|
|
165
|
+
// Build the query
|
|
166
|
+
tmpQuery.buildReadQuery();
|
|
167
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
168
|
+
libFable.log.trace('Select Query', tmpQuery.query);
|
|
169
|
+
Expect(tmpQuery.query.body)
|
|
170
|
+
.to.equal('Animals/LiteExtended/Name,Age,Cost/FilteredTo/FBV~Age~EQ~25~FOP~0~(~0~FBV~Color~EQ~Red~FBVOR~Color~EQ~Green~FCP~0~)~0~FBV~Description~NN~0~FBV~IDOffice~INN~10,11,15,18,22~FSF~Age~ASC~0/0/100');
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
test
|
|
174
|
+
(
|
|
175
|
+
'Update Query',
|
|
176
|
+
function()
|
|
177
|
+
{
|
|
178
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
179
|
+
.setLogLevel(5)
|
|
180
|
+
.setScope('Animal')
|
|
181
|
+
.addRecord({Age:15,Color:'Brown'});
|
|
182
|
+
|
|
183
|
+
// Build the query
|
|
184
|
+
tmpQuery.buildUpdateQuery();
|
|
185
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
186
|
+
libFable.log.trace('Update Query', tmpQuery.query);
|
|
187
|
+
Expect(tmpQuery.query.body)
|
|
188
|
+
.to.equal('Animal');
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
test
|
|
192
|
+
(
|
|
193
|
+
'Bad Update Query',
|
|
194
|
+
function()
|
|
195
|
+
{
|
|
196
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints');
|
|
197
|
+
|
|
198
|
+
// Build the query
|
|
199
|
+
tmpQuery.buildUpdateQuery();
|
|
200
|
+
tmpQuery.addRecord({});
|
|
201
|
+
tmpQuery.buildUpdateQuery();
|
|
202
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
203
|
+
libFable.log.trace('Update Query', tmpQuery.query);
|
|
204
|
+
Expect(tmpQuery.query.body)
|
|
205
|
+
.to.equal(false);
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
test
|
|
209
|
+
(
|
|
210
|
+
'Delete Query',
|
|
211
|
+
function()
|
|
212
|
+
{
|
|
213
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
214
|
+
.setScope('Animal')
|
|
215
|
+
.addFilter('IDAnimal', 10);
|
|
216
|
+
|
|
217
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
218
|
+
// Build the query
|
|
219
|
+
tmpQuery.buildDeleteQuery();
|
|
220
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
221
|
+
libFable.log.trace('Delete Query', tmpQuery.query);
|
|
222
|
+
Expect(tmpQuery.query.body)
|
|
223
|
+
.to.equal('Animal/10');
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
test
|
|
227
|
+
(
|
|
228
|
+
'Count Query',
|
|
229
|
+
function()
|
|
230
|
+
{
|
|
231
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
232
|
+
.setDialect('MeadowEndpoints')
|
|
233
|
+
.setScope('Animal');
|
|
234
|
+
|
|
235
|
+
// Build the query
|
|
236
|
+
tmpQuery.buildCountQuery();
|
|
237
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
238
|
+
libFable.log.trace('Count Query', tmpQuery.query);
|
|
239
|
+
Expect(tmpQuery.query.body)
|
|
240
|
+
.to.equal('Animals/Count');
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
suite
|
|
247
|
+
(
|
|
248
|
+
'Complex Query Generation - Schemas',
|
|
249
|
+
function()
|
|
250
|
+
{
|
|
251
|
+
test
|
|
252
|
+
(
|
|
253
|
+
'Create Query',
|
|
254
|
+
function()
|
|
255
|
+
{
|
|
256
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
257
|
+
.setLogLevel(5)
|
|
258
|
+
.setDialect('MeadowEndpoints')
|
|
259
|
+
.setScope('Animal')
|
|
260
|
+
.addRecord(
|
|
261
|
+
{
|
|
262
|
+
IDAnimal:false,
|
|
263
|
+
GUIDAnimal:false,
|
|
264
|
+
CreateDate:false,
|
|
265
|
+
CreatingIDUser:false,
|
|
266
|
+
UpdateDate:false,
|
|
267
|
+
UpdatingIDUser:false,
|
|
268
|
+
Deleted:false,
|
|
269
|
+
DeletingIDUser:false,
|
|
270
|
+
DeleteDate:false,
|
|
271
|
+
Name:'Froo Froo',
|
|
272
|
+
Age:18
|
|
273
|
+
});
|
|
274
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
275
|
+
// Build the query
|
|
276
|
+
tmpQuery.buildCreateQuery();
|
|
277
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
278
|
+
libFable.log.trace('Create Query', tmpQuery.query);
|
|
279
|
+
Expect(tmpQuery.query.body)
|
|
280
|
+
.to.equal("Animal");
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
test
|
|
284
|
+
(
|
|
285
|
+
'Create Query -- with GUID specified',
|
|
286
|
+
function()
|
|
287
|
+
{
|
|
288
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
289
|
+
.setLogLevel(5)
|
|
290
|
+
.setDialect('MeadowEndpoints')
|
|
291
|
+
.setScope('Animal')
|
|
292
|
+
.addRecord(
|
|
293
|
+
{
|
|
294
|
+
IDAnimal:false,
|
|
295
|
+
GUIDAnimal:'0xabcdef',
|
|
296
|
+
CreateDate:false,
|
|
297
|
+
CreatingIDUser:false,
|
|
298
|
+
UpdateDate:false,
|
|
299
|
+
UpdatingIDUser:false,
|
|
300
|
+
Deleted:false,
|
|
301
|
+
DeletingIDUser:false,
|
|
302
|
+
DeleteDate:false,
|
|
303
|
+
Name:'Froo Froo',
|
|
304
|
+
Age:18
|
|
305
|
+
});
|
|
306
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
307
|
+
// Build the query
|
|
308
|
+
tmpQuery.buildCreateQuery();
|
|
309
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
310
|
+
libFable.log.trace('Create Query', tmpQuery.query);
|
|
311
|
+
Expect(tmpQuery.query.body)
|
|
312
|
+
.to.equal("Animal");
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
test
|
|
316
|
+
(
|
|
317
|
+
'Create Query - with AutoIdentity disabled',
|
|
318
|
+
function()
|
|
319
|
+
{
|
|
320
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
321
|
+
.setLogLevel(5)
|
|
322
|
+
.setDialect('MeadowEndpoints')
|
|
323
|
+
.setScope('Animal')
|
|
324
|
+
.setDisableAutoIdentity(true)
|
|
325
|
+
.setDisableDeleteTracking(true)
|
|
326
|
+
.setDisableAutoDateStamp(true)
|
|
327
|
+
.setDisableAutoUserStamp(true)
|
|
328
|
+
.addRecord(
|
|
329
|
+
{
|
|
330
|
+
IDAnimal:false,
|
|
331
|
+
GUIDAnimal:false,
|
|
332
|
+
CreateDate:false,
|
|
333
|
+
CreatingIDUser:false,
|
|
334
|
+
UpdateDate:false,
|
|
335
|
+
UpdatingIDUser:false,
|
|
336
|
+
Deleted:false,
|
|
337
|
+
DeletingIDUser:false,
|
|
338
|
+
DeleteDate:false,
|
|
339
|
+
Name:'Froo Froo',
|
|
340
|
+
Age:18
|
|
341
|
+
});
|
|
342
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
343
|
+
// Build the query
|
|
344
|
+
tmpQuery.buildCreateQuery();
|
|
345
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
346
|
+
libFable.log.trace('Create Query (AutoIdentity disabled)', tmpQuery.query);
|
|
347
|
+
Expect(tmpQuery.query.body)
|
|
348
|
+
.to.equal("Animal/WithFlags/DisableAutoDateStamp,DisableDeleteTracking,DisableAutoIdentity,DisableAutoUserStamp");
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
test
|
|
352
|
+
(
|
|
353
|
+
'Complex Read Query 2, verify checking Deleted bit with no filters',
|
|
354
|
+
function()
|
|
355
|
+
{
|
|
356
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
357
|
+
.setDialect('MeadowEndpoints')
|
|
358
|
+
.setScope('Animal')
|
|
359
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
360
|
+
.setCap(100);
|
|
361
|
+
|
|
362
|
+
//Use a schema that already defines a deleted bit
|
|
363
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
364
|
+
// Build the query
|
|
365
|
+
tmpQuery.buildReadQuery();
|
|
366
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
367
|
+
libFable.log.trace('Select Query', tmpQuery.query);
|
|
368
|
+
Expect(tmpQuery.query.body)
|
|
369
|
+
.to.equal('Animals/LiteExtended/Name,Age,Cost/0/100');
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
test
|
|
373
|
+
(
|
|
374
|
+
'Complex Read Query 2, manually checking Deleted bit with Schema that has one',
|
|
375
|
+
function()
|
|
376
|
+
{
|
|
377
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
378
|
+
.setDialect('MeadowEndpoints')
|
|
379
|
+
.setScope('Animal')
|
|
380
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
381
|
+
.setCap(100)
|
|
382
|
+
.addFilter('Age', '25')
|
|
383
|
+
.addFilter('', '', '(')
|
|
384
|
+
.addFilter('Color', 'Red')
|
|
385
|
+
.addFilter('Color', 'Green', '=', 'OR')
|
|
386
|
+
.addFilter('', '', ')')
|
|
387
|
+
.addFilter('Description', '', 'IS NOT NULL')
|
|
388
|
+
.addFilter('IDOffice', [10, 11, 15, 18, 22], 'IN')
|
|
389
|
+
.addFilter('Deleted', '1');
|
|
390
|
+
|
|
391
|
+
//Use a schema that already defines a deleted bit
|
|
392
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
393
|
+
// Build the query
|
|
394
|
+
tmpQuery.buildReadQuery();
|
|
395
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
396
|
+
libFable.log.trace('Select Query', tmpQuery.query);
|
|
397
|
+
Expect(tmpQuery.query.body)
|
|
398
|
+
.to.equal('Animals/LiteExtended/Name,Age,Cost/FilteredTo/FBV~Age~EQ~25~FOP~0~(~0~FBV~Color~EQ~Red~FBVOR~Color~EQ~Green~FCP~0~)~0~FBV~Description~NN~0~FBV~IDOffice~INN~10,11,15,18,22~FBV~Deleted~EQ~1/0/100');
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
test
|
|
402
|
+
(
|
|
403
|
+
'Complex Read Query 2, delete tracking disabled',
|
|
404
|
+
function()
|
|
405
|
+
{
|
|
406
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
407
|
+
.setDialect('MeadowEndpoints')
|
|
408
|
+
.setScope('Animal')
|
|
409
|
+
.setDisableDeleteTracking(true)
|
|
410
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
411
|
+
.setCap(100);
|
|
412
|
+
|
|
413
|
+
//Use a schema that already defines a deleted bit
|
|
414
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
415
|
+
// Build the query
|
|
416
|
+
tmpQuery.buildReadQuery();
|
|
417
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
418
|
+
libFable.log.trace('Select Query', tmpQuery.query);
|
|
419
|
+
Expect(tmpQuery.query.body)
|
|
420
|
+
.to.equal('Animals/LiteExtended/Name,Age,Cost/FilteredTo/FBV~Deleted~GE~0/0/100');
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
test
|
|
424
|
+
(
|
|
425
|
+
'Delete Query with Filters',
|
|
426
|
+
function()
|
|
427
|
+
{
|
|
428
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
429
|
+
.setScope('Animal')
|
|
430
|
+
.addFilter('IDAnimal', 10);
|
|
431
|
+
//Perform delete with no record specified, but has a Deleted bit in the schema
|
|
432
|
+
|
|
433
|
+
//Use a schema that already defines a deleted bit
|
|
434
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
435
|
+
|
|
436
|
+
// Build the query
|
|
437
|
+
tmpQuery.buildDeleteQuery();
|
|
438
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
439
|
+
libFable.log.trace('Delete Query', tmpQuery.query);
|
|
440
|
+
Expect(tmpQuery.query.body)
|
|
441
|
+
.to.equal('Animal/10');
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
test
|
|
445
|
+
(
|
|
446
|
+
'Update Query -- without Deleted',
|
|
447
|
+
function()
|
|
448
|
+
{
|
|
449
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
450
|
+
.setLogLevel(5)
|
|
451
|
+
.setScope('Animal')
|
|
452
|
+
.addRecord({
|
|
453
|
+
IDAnimal:82,
|
|
454
|
+
GUIDAnimal:'1111-2222-3333-4444-5555-6666-7777',
|
|
455
|
+
CreateDate:false,
|
|
456
|
+
CreatingIDUser:false,
|
|
457
|
+
UpdateDate:false,
|
|
458
|
+
UpdatingIDUser:false,
|
|
459
|
+
Name:'Froo Froo',
|
|
460
|
+
Age:18
|
|
461
|
+
});
|
|
462
|
+
tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
|
|
463
|
+
// Build the query
|
|
464
|
+
tmpQuery.buildUpdateQuery();
|
|
465
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
466
|
+
libFable.log.trace('Update Query', tmpQuery.query);
|
|
467
|
+
Expect(tmpQuery.query.body)
|
|
468
|
+
.to.equal('Animal');
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
test
|
|
472
|
+
(
|
|
473
|
+
'Update Query -- without Deleted, UpdateDate and UpdatingIDUser',
|
|
474
|
+
function()
|
|
475
|
+
{
|
|
476
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
477
|
+
.setLogLevel(5)
|
|
478
|
+
.setScope('Animal')
|
|
479
|
+
.setDisableAutoUserStamp(true)
|
|
480
|
+
.setDisableAutoDateStamp(true)
|
|
481
|
+
.addRecord({
|
|
482
|
+
IDAnimal:82,
|
|
483
|
+
GUIDAnimal:'1111-2222-3333-4444-5555-6666-7777',
|
|
484
|
+
CreateDate:false,
|
|
485
|
+
CreatingIDUser:false,
|
|
486
|
+
UpdateDate:false,
|
|
487
|
+
UpdatingIDUser:false,
|
|
488
|
+
Name:'Froo Froo',
|
|
489
|
+
Age:18
|
|
490
|
+
});
|
|
491
|
+
tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
|
|
492
|
+
// Build the query
|
|
493
|
+
tmpQuery.buildUpdateQuery();
|
|
494
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
495
|
+
libFable.log.trace('Update Query', tmpQuery.query);
|
|
496
|
+
Expect(tmpQuery.query.body)
|
|
497
|
+
.to.equal('Animal/WithFlags/DisableAutoDateStamp,DisableAutoUserStamp');
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
test
|
|
501
|
+
(
|
|
502
|
+
'Delete Query -- without Deleted',
|
|
503
|
+
function()
|
|
504
|
+
{
|
|
505
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
506
|
+
.setLogLevel(5)
|
|
507
|
+
.setScope('Animal')
|
|
508
|
+
.addFilter('IDAnimal', 9);
|
|
509
|
+
tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
|
|
510
|
+
// Build the query
|
|
511
|
+
tmpQuery.buildDeleteQuery();
|
|
512
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
513
|
+
libFable.log.trace('Delete Query', tmpQuery.query);
|
|
514
|
+
Expect(tmpQuery.query.body)
|
|
515
|
+
.to.equal('Animal/9');
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
test
|
|
519
|
+
(
|
|
520
|
+
'Update Query',
|
|
521
|
+
function()
|
|
522
|
+
{
|
|
523
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
524
|
+
.setLogLevel(5)
|
|
525
|
+
.setScope('Animal')
|
|
526
|
+
.addFilter('Deleted', 0) //cover case where this can be overridden instead of automatically added
|
|
527
|
+
.addRecord({
|
|
528
|
+
IDAnimal:82,
|
|
529
|
+
GUIDAnimal:'1111-2222-3333-4444-5555-6666-7777',
|
|
530
|
+
CreateDate:false,
|
|
531
|
+
CreatingIDUser:false,
|
|
532
|
+
UpdateDate:false,
|
|
533
|
+
UpdatingIDUser:false,
|
|
534
|
+
Deleted:false,
|
|
535
|
+
DeletingIDUser:false,
|
|
536
|
+
DeleteDate:false,
|
|
537
|
+
Name:'Froo Froo',
|
|
538
|
+
Age:18
|
|
539
|
+
});
|
|
540
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
541
|
+
// Build the query
|
|
542
|
+
tmpQuery.buildUpdateQuery();
|
|
543
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
544
|
+
libFable.log.trace('Update Query', tmpQuery.query);
|
|
545
|
+
Expect(tmpQuery.query.body)
|
|
546
|
+
.to.equal('Animal');
|
|
547
|
+
}
|
|
548
|
+
);
|
|
549
|
+
test
|
|
550
|
+
(
|
|
551
|
+
'Delete Query',
|
|
552
|
+
function()
|
|
553
|
+
{
|
|
554
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
555
|
+
.setLogLevel(5)
|
|
556
|
+
.setScope('Animal')
|
|
557
|
+
.addFilter('IDAnimal', 9);
|
|
558
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
559
|
+
// Build the query
|
|
560
|
+
tmpQuery.buildDeleteQuery();
|
|
561
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
562
|
+
libFable.log.trace('Delete Query', tmpQuery.query);
|
|
563
|
+
Expect(tmpQuery.query.body)
|
|
564
|
+
.to.equal('Animal/9');
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
test
|
|
568
|
+
(
|
|
569
|
+
'Delete Query with Delete Tracking Disabled',
|
|
570
|
+
function()
|
|
571
|
+
{
|
|
572
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MeadowEndpoints')
|
|
573
|
+
.setLogLevel(5)
|
|
574
|
+
.setScope('Animal')
|
|
575
|
+
.setDisableDeleteTracking(true)
|
|
576
|
+
.addFilter('IDAnimal', 9);
|
|
577
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
578
|
+
// Build the query
|
|
579
|
+
tmpQuery.buildDeleteQuery();
|
|
580
|
+
// This is the query generated by the MeadowEndpoints dialect
|
|
581
|
+
libFable.log.trace('Delete Query', tmpQuery.query);
|
|
582
|
+
Expect(tmpQuery.query.body)
|
|
583
|
+
.to.equal('Animal/9');
|
|
584
|
+
}
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
);
|
|
@@ -333,13 +333,32 @@ suite
|
|
|
333
333
|
// This is the query generated by the MySQL dialect
|
|
334
334
|
libFable.log.trace('Count Query', tmpQuery.query);
|
|
335
335
|
Expect(tmpQuery.query.body)
|
|
336
|
-
.to.equal('SELECT COUNT(
|
|
336
|
+
.to.equal('SELECT COUNT(*) AS RowCount FROM `Animal`;');
|
|
337
337
|
}
|
|
338
338
|
);
|
|
339
339
|
test
|
|
340
340
|
(
|
|
341
341
|
'Count Distinct Query',
|
|
342
342
|
function()
|
|
343
|
+
{
|
|
344
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
345
|
+
.setDialect('MySQL')
|
|
346
|
+
.setScope('Animal');
|
|
347
|
+
tmpQuery.setDistinct(true);
|
|
348
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
349
|
+
|
|
350
|
+
// Build the query
|
|
351
|
+
tmpQuery.buildCountQuery();
|
|
352
|
+
// This is the query generated by the MySQL dialect
|
|
353
|
+
libFable.log.trace('Count Distinct Query', tmpQuery.query);
|
|
354
|
+
Expect(tmpQuery.query.body)
|
|
355
|
+
.to.equal('SELECT COUNT(DISTINCT `Animal`.`IDAnimal`, `Animal`.`GUIDAnimal`, `Animal`.`CreateDate`, `Animal`.`CreatingIDUser`, `Animal`.`UpdateDate`, `Animal`.`UpdatingIDUser`, `Animal`.`Deleted`, `Animal`.`DeletingIDUser`, `Animal`.`DeleteDate`) AS RowCount FROM `Animal` WHERE `Animal`.Deleted = :Deleted_w0;');
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
test
|
|
359
|
+
(
|
|
360
|
+
'Count Distinct Query without Schema',
|
|
361
|
+
function()
|
|
343
362
|
{
|
|
344
363
|
var tmpQuery = libFoxHound.new(libFable)
|
|
345
364
|
.setDialect('MySQL')
|
|
@@ -351,7 +370,7 @@ suite
|
|
|
351
370
|
// This is the query generated by the MySQL dialect
|
|
352
371
|
libFable.log.trace('Count Distinct Query', tmpQuery.query);
|
|
353
372
|
Expect(tmpQuery.query.body)
|
|
354
|
-
.to.equal('SELECT COUNT(
|
|
373
|
+
.to.equal('SELECT COUNT(*) AS RowCount FROM `Animal`;');
|
|
355
374
|
}
|
|
356
375
|
);
|
|
357
376
|
test
|