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.
@@ -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
+ }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foxhound",
3
- "version": "1.0.32",
3
+ "version": "1.0.33",
4
4
  "description": "A Database Query generation library.",
5
5
  "main": "source/FoxHound.js",
6
6
  "scripts": {
@@ -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(${tmpOptDistinct}${tmpFieldList}) AS RowCount FROM${tmpTableName}${tmpWhere};`;
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
- * @return: {String} Returns the field list clause
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
- return generateTableName(pParameters) + '.*';
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.replace('`', '').split('.');
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
- const tmpOptDistinct = pParameters.distinct ? 'DISTINCT' : '';
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 Animal WHERE `Age` = :Age_w0;');
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( *) AS RowCount FROM Animal;');
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( `Animal`.*) AS RowCount FROM `Animal`;');
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(DISTINCT `Animal`.*) AS RowCount FROM `Animal`;');
373
+ .to.equal('SELECT COUNT(*) AS RowCount FROM `Animal`;');
355
374
  }
356
375
  );
357
376
  test