meadow-endpoints 4.0.5 → 4.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CONTRIBUTING.md +50 -0
  2. package/Dockerfile_LUXURYCode +1 -39
  3. package/README.md +172 -51
  4. package/dist/indoctrinate_content_staging/Indoctrinate-Catalog-AppData.json +4548 -0
  5. package/docs/README.md +326 -0
  6. package/docs/_sidebar.md +37 -0
  7. package/docs/crud/README.md +220 -0
  8. package/docs/crud/count.md +168 -0
  9. package/docs/crud/create.md +194 -0
  10. package/docs/crud/delete.md +237 -0
  11. package/docs/crud/read.md +324 -0
  12. package/docs/crud/schema.md +349 -0
  13. package/docs/crud/update.md +203 -0
  14. package/docs/css/docuserve.css +73 -0
  15. package/docs/index.html +39 -0
  16. package/docs/retold-catalog.json +177 -0
  17. package/docs/retold-keyword-index.json +6720 -0
  18. package/package.json +24 -19
  19. package/source/Meadow-Endpoints.js +8 -1
  20. package/source/controller/Meadow-Endpoints-Controller-Base.js +14 -7
  21. package/source/controller/components/Meadow-Endpoints-Controller-BehaviorInjection.js +4 -1
  22. package/source/controller/components/Meadow-Endpoints-Controller-Error.js +4 -1
  23. package/source/controller/components/Meadow-Endpoints-Controller-Log.js +4 -1
  24. package/source/controller/utility/Meadow-Endpoints-Filter-Parser.js +13 -204
  25. package/source/controller/utility/Meadow-Endpoints-Session-Marshaler.js +10 -6
  26. package/source/controller/utility/Meadow-Endpoints-Stream-RecordArray.js +5 -2
  27. package/source/endpoints/delete/Meadow-Endpoint-Undelete.js +2 -2
  28. package/source/endpoints/read/Meadow-Endpoint-ReadDistinctList.js +3 -1
  29. package/source/endpoints/read/Meadow-Endpoint-ReadLiteList.js +3 -1
  30. package/source/endpoints/read/Meadow-Endpoint-ReadSelectList.js +3 -1
  31. package/source/endpoints/read/Meadow-Endpoint-Reads.js +3 -1
  32. package/source/endpoints/read/Meadow-Endpoint-ReadsBy.js +3 -1
  33. package/source/endpoints/update/Meadow-Operation-Update.js +9 -1
  34. package/test/MeadowEndpoints_basic_tests.js +1580 -65
  35. package/tsconfig.json +13 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meadow-endpoints",
3
- "version": "4.0.5",
3
+ "version": "4.0.9",
4
4
  "description": "Automatic API endpoints for Meadow data.",
5
5
  "main": "source/Meadow-Endpoints.js",
6
6
  "scripts": {
@@ -12,7 +12,8 @@
12
12
  "tests": "./node_modules/.bin/mocha -u tdd --exit -R spec --grep",
13
13
  "build": "./node_modules/.bin/gulp build",
14
14
  "docker-dev-build-image": "docker build ./ -f Dockerfile_LUXURYCode -t retold/meadow-endpoints:local",
15
- "docker-dev-run": "docker run -it -d --name meadow-endpoints-dev -p 12343:8080 -p 12305:3306 -p 18086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/meadow-endpoints\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold/meadow-endpoints:local"
15
+ "docker-dev-run": "docker run -it -d --name meadow-endpoints-dev -p 12343:8080 -p 12305:3306 -p 18086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/meadow-endpoints\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold/meadow-endpoints:local",
16
+ "check": "npx -p typescript tsc --noEmit"
16
17
  },
17
18
  "mocha": {
18
19
  "diff": true,
@@ -46,31 +47,35 @@
46
47
  },
47
48
  "homepage": "https://github.com/stevenvelozo/meadow-endpoints",
48
49
  "devDependencies": {
49
- "alasql": "^4.0.0",
50
- "browserify": "^17.0.0",
51
- "chai": "4.3.7",
52
- "chance": "^1.1.11",
53
- "gulp": "^4.0.2",
50
+ "alasql": "^4.17.0",
51
+ "better-sqlite3": "^12.6.2",
52
+ "browserify": "^17.0.1",
53
+ "chai": "6.2.2",
54
+ "chance": "^1.1.13",
55
+ "gulp": "^5.0.1",
54
56
  "gulp-babel": "^8.0.0",
55
57
  "gulp-sourcemaps": "^3.0.0",
56
58
  "gulp-terser": "^2.1.0",
57
59
  "gulp-util": "^3.0.8",
58
- "mocha": "10.2.0",
59
- "mysql2": "^3.3.1",
60
- "nyc": "^15.1.0",
61
- "orator-serviceserver-restify": "^1.0.4",
62
- "papaparse": "^5.4.1",
63
- "supertest": "^6.3.3",
60
+ "meadow-connection-sqlite": "file:../meadow-connection-sqlite",
61
+ "mocha": "11.7.5",
62
+ "mysql2": "^3.16.3",
63
+ "nyc": "^17.1.0",
64
+ "orator-serviceserver-restify": "^2.0.5",
65
+ "papaparse": "^5.5.3",
66
+ "supertest": "^7.2.2",
67
+ "typescript": "^5.9.3",
64
68
  "vinyl-buffer": "^1.0.1",
65
69
  "vinyl-source-stream": "^2.0.0",
66
- "why-is-node-running": "^2.2.2"
70
+ "why-is-node-running": "^3.2.2"
67
71
  },
68
72
  "dependencies": {
73
+ "async": "3.2.6",
74
+ "fable": "^3.1.55",
69
75
  "JSONStream": "^1.3.5",
70
- "async": "3.2.4",
71
- "fable": "^3.0.37",
72
- "meadow": "^2.0.4",
73
- "orator": "^3.0.11",
74
- "underscore": "^1.13.6"
76
+ "meadow": "^2.0.22",
77
+ "meadow-filter": "^1.0.10",
78
+ "orator": "^5.0.2",
79
+ "underscore": "^1.13.7"
75
80
  }
76
81
  }
@@ -101,10 +101,17 @@ class MeadowEndpoints
101
101
  });
102
102
  }
103
103
 
104
+ /**
105
+ * @return {import('./controller/Meadow-Endpoints-Controller-Base.js')} The controller instance
106
+ */
104
107
  get controller()
105
108
  {
106
109
  return this._Controller;
107
110
  }
111
+
112
+ /**
113
+ * @param {import('./controller/Meadow-Endpoints-Controller-Base.js')} pController - The controller instance
114
+ */
108
115
  set controller(pController)
109
116
  {
110
117
  this._Controller = pController;
@@ -231,4 +238,4 @@ module.exports = MeadowEndpoints;
231
238
  module.exports.new = autoConstruct;
232
239
 
233
240
  module.exports.Meadow = libMeadow;
234
- module.exports.BaseController = libMeadowEndpointsControllerBase;
241
+ module.exports.BaseController = libMeadowEndpointsControllerBase;
@@ -11,25 +11,29 @@ const libMeadowEndpointsStreamRecordArray = require('./utility/Meadow-Endpoints-
11
11
 
12
12
  class MeadowEndpointControllerBase
13
13
  {
14
+ /**
15
+ * @param {import('../Meadow-Endpoints.js')} pMeadowEndpoints
16
+ */
14
17
  constructor(pMeadowEndpoints)
15
18
  {
16
19
  this.DAL = pMeadowEndpoints.DAL;
17
- this.ControllerOptions = pMeadowEndpoints._ControllerOptions
20
+ this.ControllerOptions = pMeadowEndpoints._ControllerOptions;
18
21
 
19
22
  // Application Services
20
- this._Settings = false;
21
- this._LogController = false;
23
+ this._Settings = null;
24
+ this._LogController = null;
22
25
 
23
26
  // Logic and Behavior
24
- this._BehaviorInjectionController = false;
25
- this._ErrorController = false;
27
+ this._BehaviorInjectionController = null;
28
+ this._ErrorController = null;
26
29
 
27
30
  // Internal async utility functions
28
31
  this.waterfall = this.DAL.fable.Utility.waterfall;
29
32
  this.eachLimit = this.DAL.fable.Utility.eachLimit;
30
33
  this.extend = this.DAL.fable.Utility.extend;
31
34
 
32
- if ((typeof(pControllerOptions) != 'object') || pControllerOptions.hasOwnProperty('ControllerClass'))
35
+ //FIXME: not sure what this is supposed to do, it was broken before
36
+ //if ((typeof(this.ControllerOptions) != 'object') || this.ControllerOptions.hasOwnProperty('ControllerClass'))
33
37
  {
34
38
  this.initializeDefaultUnsetControllers(this);
35
39
  }
@@ -40,6 +44,9 @@ class MeadowEndpointControllerBase
40
44
  this._StreamRecordArray = new libMeadowEndpointsStreamRecordArray(this);
41
45
  }
42
46
 
47
+ /**
48
+ * @param {import('./Meadow-Endpoints-Controller-Base.js')} pController
49
+ */
43
50
  initializeDefaultUnsetControllers(pController)
44
51
  {
45
52
  // Application Services
@@ -158,4 +165,4 @@ module.exports.BaseBehaviorInjectionController = libBaseBehaviorInjectionControl
158
165
 
159
166
  module.exports.BaseFilterParser = libMeadowEndpointsFilterParser;
160
167
  module.exports.BaseSessionMarshaler = libMeadowEndpointsSessionMarshaler;
161
- module.exports.BaseStreamRecordArray = libMeadowEndpointsStreamRecordArray;
168
+ module.exports.BaseStreamRecordArray = libMeadowEndpointsStreamRecordArray;
@@ -4,6 +4,9 @@ const libUnderscore = require('underscore');
4
4
 
5
5
  class MeadowEndpointsControllerBehaviorInjectionBase
6
6
  {
7
+ /**
8
+ * @param {import('../Meadow-Endpoints-Controller-Base.js')} pController
9
+ */
7
10
  constructor(pController)
8
11
  {
9
12
  this._Controller = pController;
@@ -127,4 +130,4 @@ class MeadowEndpointsControllerBehaviorInjectionBase
127
130
  }
128
131
  }
129
132
 
130
- module.exports = MeadowEndpointsControllerBehaviorInjectionBase;
133
+ module.exports = MeadowEndpointsControllerBehaviorInjectionBase;
@@ -1,5 +1,8 @@
1
1
  class MeadowEndpointsControllerErrorBase
2
2
  {
3
+ /**
4
+ * @param {import('../Meadow-Endpoints-Controller-Base.js')} pController
5
+ */
3
6
  constructor(pController)
4
7
  {
5
8
  this._Controller = pController;
@@ -115,4 +118,4 @@ class MeadowEndpointsControllerErrorBase
115
118
  }
116
119
  }
117
120
 
118
- module.exports = MeadowEndpointsControllerErrorBase;
121
+ module.exports = MeadowEndpointsControllerErrorBase;
@@ -1,5 +1,8 @@
1
1
  class MeadowEndpointsControllerLogBase
2
2
  {
3
+ /**
4
+ * @param {import('../Meadow-Endpoints-Controller-Base.js')} pController
5
+ */
3
6
  constructor(pController)
4
7
  {
5
8
  this._Controller = pController;
@@ -100,4 +103,4 @@ class MeadowEndpointsControllerLogBase
100
103
  }
101
104
  }
102
105
 
103
- module.exports = MeadowEndpointsControllerLogBase;
106
+ module.exports = MeadowEndpointsControllerLogBase;
@@ -6,220 +6,29 @@
6
6
  * @author Steven Velozo <steven@velozo.com>
7
7
  * @module Meadow
8
8
  */
9
- /**
10
- * Parse GET-passed Filter Strings, turn the results into proper Meadow query stanzas
11
-
12
- Take the filter and return an array of filter instructions
13
- Basic instruction anatomy:
14
- INSTRUCTION~FIELD~OPERATOR~VALUE
15
- FOP - Filter Open Paren
16
- FOP~0~(~0
17
- FCP - Filter Close Paren
18
- FCP~0~)~0
19
- FBV - Filter By Value (left-side AND connected)
20
- FBV~Category~EQ~Books
21
- Possible comparisons:
22
- * EQ - Equals To (=)
23
- * NE - Not Equals To (!=)
24
- * GT - Greater Than (>)
25
- * GE - Greater Than or Equals To (>=)
26
- * LT - Less Than (<)
27
- * LE - Less Than or Equals To (<=)
28
- * LK - Like (Like)
29
- * IN - Is NULL
30
- * NN - Is NOT NULL
31
- * INN - IN list
32
- FBVOR - Filter By Value (left-side OR connected)
33
- FBL - Filter By List (value list, separated by commas)
34
- FBL~Category~EQ~Books,Movies
35
- FBD - Filter by Date (exclude time)
36
- FBD~UpdateDate~EQ~2015-10-01
37
- FSF - Filter Sort Field
38
- FSF~Category~ASC~0
39
- FSF~Category~DESC~0
40
- FDST - Filter by Distinct (adds distinct keyword to Read and Count queries)
41
- FDST~0~0~0~
42
9
 
43
- This means: FBV~Category~EQ~Books~FBV~PublishedYear~GT~2000~FSF~PublishedYear~DESC~0
44
- Filters down to ALL BOOKS PUBLUSHED AFTER 2000 IN DESCENDING ORDER
45
- */
10
+ const { parse } = require('meadow-filter');
46
11
 
47
12
  class MeadowEndpointsFilterParser
48
13
  {
14
+ /**
15
+ * @param {import('../Meadow-Endpoints-Controller-Base.js')} pController - The controller instance to which this parser belongs.
16
+ */
49
17
  constructor(pController)
50
18
  {
51
19
  this._Controller = pController;
52
20
  }
53
21
 
54
- // Get the comparison operator for use in a query stanza
55
- getFilterComparisonOperator(pFilterOperator)
56
- {
57
- let tmpOperator = '=';
58
- switch(pFilterOperator)
59
- {
60
- case 'EQ':
61
- tmpOperator = '=';
62
- break;
63
- case 'NE':
64
- tmpOperator = '!=';
65
- break;
66
- case 'GT':
67
- tmpOperator = '>';
68
- break;
69
- case 'GE':
70
- tmpOperator = '>=';
71
- break;
72
- case 'LT':
73
- tmpOperator = '<';
74
- break;
75
- case 'LE':
76
- tmpOperator = '<=';
77
- break;
78
- case 'LK':
79
- tmpOperator = 'LIKE';
80
- break;
81
- case 'NLK':
82
- tmpOperator = 'NOT LIKE';
83
- break;
84
- case 'IN':
85
- tmpOperator = 'IS NULL';
86
- break;
87
- case 'NN':
88
- tmpOperator = 'IS NOT NULL';
89
- break;
90
- case 'INN':
91
- tmpOperator = 'IN';
92
- break;
93
- case 'FOP':
94
- tmpOperator = '(';
95
- break;
96
- case 'FCP':
97
- tmpOperator = ')';
98
- break;
99
- }
100
- return tmpOperator;
101
- }
102
-
103
- addFilterStanzaToQuery(pFilterStanza, pQuery)
104
- {
105
- if (!pFilterStanza.Instruction)
106
- {
107
- return false;
108
- }
109
-
110
- switch(pFilterStanza.Instruction)
111
- {
112
- case 'FBV': // Filter by Value (left-side AND)
113
- pQuery.addFilter(pFilterStanza.Field, pFilterStanza.Value, this.getFilterComparisonOperator(pFilterStanza.Operator), 'AND');
114
- break;
115
-
116
- case 'FBVOR': // Filter by Value (left-side OR)
117
- pQuery.addFilter(pFilterStanza.Field, pFilterStanza.Value, this.getFilterComparisonOperator(pFilterStanza.Operator), 'OR');
118
- break;
119
-
120
- case 'FBL': // Filter by List (left-side AND)
121
- // Just split the value by comma for now. May want to revisit better characters or techniques later.
122
- pQuery.addFilter(pFilterStanza.Field, pFilterStanza.Value.split(','), this.getFilterComparisonOperator(pFilterStanza.Operator), 'AND');
123
- break;
124
-
125
- case 'FBLOR': // Filter by List (left-side OR)
126
- // Just split the value by comma for now. May want to revisit better characters or techniques later.
127
- pQuery.addFilter(pFilterStanza.Field, pFilterStanza.Value.split(','), this.getFilterComparisonOperator(pFilterStanza.Operator), 'OR');
128
- break;
129
-
130
- case 'FBD': // Filter by Date (exclude time)
131
- pQuery.addFilter(`DATE(${pFilterStanza.Field})`, pFilterStanza.Value.split(','), this.getFilterComparisonOperator(pFilterStanza.Operator), 'AND', pFilterStanza.Field);
132
- break;
133
-
134
- case 'FBDOR': // Filter by Date (exclude time)
135
- pQuery.addFilter(`DATE(${pFilterStanza.Field})`, pFilterStanza.Value.split(','), this.getFilterComparisonOperator(pFilterStanza.Operator), 'OR', pFilterStanza.Field);
136
- break;
137
-
138
- case 'FSF': // Filter Sort Field
139
- const tmpSortDirection = (pFilterStanza.Operator === 'DESC') ? 'Descending' : 'Ascending';
140
- pQuery.addSort({ Column: pFilterStanza.Field, Direction: tmpSortDirection });
141
- break;
142
-
143
- case 'FOP': // Filter Open Paren
144
- pQuery.addFilter('', '', '(');
145
- break;
146
-
147
- case 'FCP': // Filter Close Paren
148
- pQuery.addFilter('', '', ')');
149
- break;
150
-
151
- case 'FDST': // Filter Distinct
152
- // ensure we don't break if using an older foxhound version
153
- if (pQuery.setDistinct)
154
- {
155
- pQuery.setDistinct(true);
156
- }
157
- break;
158
-
159
- default:
160
- //console.log('Unparsable filter stanza.');
161
- return false;
162
- break;
163
- }
164
-
165
- // Be paranoid about the instruction
166
- pFilterStanza.Instruction = false;
167
- return true;
168
- };
169
-
22
+ /**
23
+ * @param {string} pFilterString - The filter string to parse.
24
+ * @param {any} pQuery - The foxhound query object to populate.
25
+ *
26
+ * @return {boolean} - True if the filter was parsed successfully, false otherwise.
27
+ */
170
28
  parseFilter(pFilterString, pQuery)
171
29
  {
172
- if (typeof(pFilterString) !== 'string')
173
- {
174
- return false;
175
- }
176
-
177
- const tmpFilterTerms = pFilterString.split('~');
178
-
179
- if (tmpFilterTerms.length < 4)
180
- {
181
- return true;
182
- }
183
-
184
- let tmpFilterStanza = { Instruction: false };
185
-
186
- for (let i = 0; i < tmpFilterTerms.length; i++)
187
- {
188
- switch(i % 4)
189
- {
190
- case 0: // INSTRUCTION
191
- this.addFilterStanzaToQuery(tmpFilterStanza, pQuery);
192
- //console.log(i+' Instruction: '+tmpFilterTerms[i]);
193
- tmpFilterStanza = (
194
- {
195
- Instruction: tmpFilterTerms[i],
196
- Field: '',
197
- Operator: '',
198
- Value: ''
199
- });
200
- break;
201
-
202
- case 1: // FIELD
203
- //console.log(i+' Field: '+tmpFilterTerms[i]);
204
- tmpFilterStanza.Field = tmpFilterTerms[i];
205
- break;
206
-
207
- case 2: // OPERATOR
208
- //console.log(i+' Operator: '+tmpFilterTerms[i]);
209
- tmpFilterStanza.Operator = tmpFilterTerms[i];
210
- break;
211
-
212
- case 3: // VALUE
213
- //console.log(i+' Value: '+tmpFilterTerms[i]);
214
- tmpFilterStanza.Value = tmpFilterTerms[i];
215
- break;
216
- }
217
- }
218
-
219
- this.addFilterStanzaToQuery(tmpFilterStanza, pQuery);
220
-
221
- return true;
222
- };
30
+ return parse(pFilterString, pQuery);
31
+ }
223
32
  }
224
33
 
225
- module.exports = MeadowEndpointsFilterParser;
34
+ module.exports = MeadowEndpointsFilterParser;
@@ -1,5 +1,8 @@
1
1
  class MeadowEndpointsSessionMarshaler
2
2
  {
3
+ /**
4
+ * @param {import('../Meadow-Endpoints-Controller-Base.js')} pController
5
+ */
3
6
  constructor(pController)
4
7
  {
5
8
  this._Controller = pController;
@@ -8,11 +11,12 @@ class MeadowEndpointsSessionMarshaler
8
11
  getSessionData(pRequest)
9
12
  {
10
13
  let tmpSession = Object.assign({}, this._Controller.settings.MeadowEndpointsDefaultSessionObject);
14
+ let tmpHeaderSessionString;
11
15
 
12
16
  switch (this._Controller.settings.MeadowEndpointsSessionDataSource || 'Request')
13
17
  {
14
18
  default:
15
- this._LogController.warn(`Unknown session source configured: ${_SessionDataSource} - defaulting to Request for backward compatibility`);
19
+ this._Controller.log.warn(`Unknown session source configured: ${this._Controller.settings.MeadowEndpointsSessionDataSource} - defaulting to Request for backward compatibility`);
16
20
  case 'Request':
17
21
  // noop - already set by orator-session
18
22
  tmpSession = this._Controller.extend(tmpSession, pRequest.UserSession);
@@ -22,17 +26,17 @@ class MeadowEndpointsSessionMarshaler
22
26
  case 'Header':
23
27
  try
24
28
  {
25
- const tmpHeaderSessionString = pRequest.headers['x-trusted-session'];
29
+ tmpHeaderSessionString = pRequest.headers['x-trusted-session'];
26
30
  if (!tmpHeaderSessionString)
27
31
  {
28
32
  break;
29
33
  }
30
- tmpHeaderSession = JSON.parse(tmpHeaderSessionString);
31
- tmpSession = this._Controller.extend(tmpSession, pRequest.tmpHeaderSession);
34
+ const tmpHeaderSession = JSON.parse(tmpHeaderSessionString);
35
+ tmpSession = this._Controller.extend(tmpSession, tmpHeaderSession);
32
36
  }
33
37
  catch (pError)
34
38
  {
35
- this._LogController.error(`Meadow Endpoints attempted to process a Header Session String with value [${tmpHeaderSessionString}] and failed -- likely culprit is bad JSON.`)
39
+ this._Controller.log.error(`Meadow Endpoints attempted to process a Header Session String with value [${tmpHeaderSessionString}] and failed -- likely culprit is bad JSON.`)
36
40
  }
37
41
  break;
38
42
  }
@@ -45,4 +49,4 @@ class MeadowEndpointsSessionMarshaler
45
49
  }
46
50
  }
47
51
 
48
- module.exports = MeadowEndpointsSessionMarshaler;
52
+ module.exports = MeadowEndpointsSessionMarshaler;
@@ -6,6 +6,9 @@ const JSONStream = require('JSONStream');
6
6
 
7
7
  class MeadowEndpointsStreamRecordArray
8
8
  {
9
+ /**
10
+ * @param {import('../Meadow-Endpoints-Controller-Base.js')} pController
11
+ */
9
12
  constructor(pController)
10
13
  {
11
14
  this._Controller = pController;
@@ -53,7 +56,7 @@ class MeadowEndpointsStreamRecordArray
53
56
  libAsyncEachSeries(this.chunk(pRecords, 1000), (pRecordChunk, fNext) =>
54
57
  {
55
58
  pRecordChunk.forEach(recordJsonMarshaller.write);
56
- setImmediate(fNext);
59
+ setTimeout(fNext, 0);
57
60
  },
58
61
  (error) =>
59
62
  {
@@ -63,4 +66,4 @@ class MeadowEndpointsStreamRecordArray
63
66
  }
64
67
  }
65
68
 
66
- module.exports = MeadowEndpointsStreamRecordArray;
69
+ module.exports = MeadowEndpointsStreamRecordArray;
@@ -23,7 +23,7 @@ const doAPIEndpointUndelete = function(pRequest, pResponse, fNext)
23
23
  // TODO: Decide if we want to keep this pattern similar to Delete, or, if we want to change it to allow bulk undeletes.
24
24
  if (tmpIDRecord < 1)
25
25
  {
26
- return fStageComplete(this.ErrorHandler.getError('Record undelete failure - a valid record ID is required.', 500));
26
+ return fNext(this.ErrorHandler.getError('Record undelete failure - a valid record ID is required.', 500));
27
27
  }
28
28
 
29
29
  tmpRequestState.RecordCount = {Count:0};
@@ -105,4 +105,4 @@ const doAPIEndpointUndelete = function(pRequest, pResponse, fNext)
105
105
  );
106
106
  };
107
107
 
108
- module.exports = doAPIEndpointUndelete;
108
+ module.exports = doAPIEndpointUndelete;
@@ -16,7 +16,9 @@ const doAPIEndpointReadDistinct = function(pRequest, pResponse, fNext)
16
16
  {
17
17
  tmpRequestState.Query = this.DAL.query.setDistinct(true);
18
18
 
19
+ /** @type {number | boolean} */
19
20
  let tmpCap = false;
21
+ /** @type {number | boolean} */
20
22
  let tmpBegin = false;
21
23
  if (typeof(pRequest.params.Begin) === 'string' ||
22
24
  typeof(pRequest.params.Begin) === 'number')
@@ -89,4 +91,4 @@ const doAPIEndpointReadDistinct = function(pRequest, pResponse, fNext)
89
91
  });
90
92
  };
91
93
 
92
- module.exports = doAPIEndpointReadDistinct;
94
+ module.exports = doAPIEndpointReadDistinct;
@@ -16,7 +16,9 @@ const doAPIEndpointReadLite = function(pRequest, pResponse, fNext)
16
16
  tmpRequestState.Query = this.DAL.query;
17
17
  // TODO: Limit the query to the columns we need for the templated expression
18
18
 
19
+ /** @type {number | boolean} */
19
20
  var tmpCap = false;
21
+ /** @type {number | boolean} */
20
22
  var tmpBegin = false;
21
23
  if (typeof(pRequest.params.Begin) === 'string' ||
22
24
  typeof(pRequest.params.Begin) === 'number')
@@ -82,4 +84,4 @@ const doAPIEndpointReadLite = function(pRequest, pResponse, fNext)
82
84
  );
83
85
  };
84
86
 
85
- module.exports = doAPIEndpointReadLite;
87
+ module.exports = doAPIEndpointReadLite;
@@ -12,7 +12,9 @@ const doAPIEndpointReadSelectList = function(pRequest, pResponse, fNext)
12
12
  {
13
13
  tmpRequestState.Query = this.DAL.query;
14
14
 
15
+ /** @type {number | boolean} */
15
16
  var tmpCap = false;
17
+ /** @type {number | boolean} */
16
18
  var tmpBegin = false;
17
19
  if (typeof(pRequest.params.Begin) === 'string' ||
18
20
  typeof(pRequest.params.Begin) === 'number')
@@ -86,4 +88,4 @@ const doAPIEndpointReadSelectList = function(pRequest, pResponse, fNext)
86
88
  );
87
89
  };
88
90
 
89
- module.exports = doAPIEndpointReadSelectList;
91
+ module.exports = doAPIEndpointReadSelectList;
@@ -12,7 +12,9 @@ const doAPIEndpointReads = function(pRequest, pResponse, fNext)
12
12
  {
13
13
  tmpRequestState.Query = this.DAL.query;
14
14
 
15
+ /** @type {number | boolean} */
15
16
  var tmpCap = false;
17
+ /** @type {number | boolean} */
16
18
  var tmpBegin = false;
17
19
  if (typeof(pRequest.params.Begin) === 'string' ||
18
20
  typeof(pRequest.params.Begin) === 'number')
@@ -72,4 +74,4 @@ const doAPIEndpointReads = function(pRequest, pResponse, fNext)
72
74
  });
73
75
  };
74
76
 
75
- module.exports = doAPIEndpointReads;
77
+ module.exports = doAPIEndpointReads;
@@ -13,7 +13,9 @@ const doAPIEndpointReadsBy = function(pRequest, pResponse, fNext)
13
13
  {
14
14
  tmpRequestState.Query = this.DAL.query;
15
15
 
16
+ /** @type {number | boolean} */
16
17
  var tmpCap = false;
18
+ /** @type {number | boolean} */
17
19
  var tmpBegin = false;
18
20
  if (typeof(pRequest.params.Begin) === 'string' ||
19
21
  typeof(pRequest.params.Begin) === 'number')
@@ -97,4 +99,4 @@ const doAPIEndpointReadsBy = function(pRequest, pResponse, fNext)
97
99
  );
98
100
  };
99
101
 
100
- module.exports = doAPIEndpointReadsBy;
102
+ module.exports = doAPIEndpointReadsBy;
@@ -100,7 +100,15 @@ const doUpdate = function(pRecordToModify, pRequest, pRequestState, pResponse, f
100
100
  {
101
101
  if (pError)
102
102
  {
103
- tmpRequestState.Record.Error = pError;
103
+ // Ensure we have a record object to attach the error to
104
+ if (tmpRequestState.Record)
105
+ {
106
+ tmpRequestState.Record.Error = pError;
107
+ }
108
+ else
109
+ {
110
+ tmpRequestState.Record = { Error: pError };
111
+ }
104
112
 
105
113
  tmpRequestState.ParentRequestState.RecordUpdateError = true;
106
114
  tmpRequestState.ParentRequestState.RecordUpdateErrorObject = pError;