meadow-endpoints 3.0.7 → 4.0.2
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/Dockerfile_LUXURYCode +1 -1
- package/README.md +48 -14
- package/debug/Animal.json +62 -0
- package/debug/Harness-Configuration.json +31 -0
- package/debug/Harness.js +7 -108
- package/debug/KillHarness.sh +10 -0
- package/dist/meadowendpoints.js +4402 -0
- package/dist/meadowendpoints.min.js +92 -0
- package/dist/meadowendpoints.min.js.map +1 -0
- package/gulpfile.js +83 -0
- package/package.json +27 -15
- package/source/Meadow-Endpoints-Browser-Shim.js +14 -0
- package/source/Meadow-Endpoints.js +176 -565
- package/source/controller/Meadow-Endpoints-Controller-Base.js +161 -0
- package/source/controller/components/Meadow-Endpoints-Controller-BehaviorInjection.js +125 -0
- package/source/controller/components/Meadow-Endpoints-Controller-Error-StatusCodes.txt +189 -0
- package/source/controller/components/Meadow-Endpoints-Controller-Error.js +118 -0
- package/source/controller/components/Meadow-Endpoints-Controller-Log.js +103 -0
- package/source/controller/utility/Meadow-Endpoints-Filter-Parser.js +225 -0
- package/source/controller/utility/Meadow-Endpoints-Session-Marshaler.js +48 -0
- package/source/controller/utility/Meadow-Endpoints-Stream-RecordArray.js +66 -0
- package/source/endpoints/count/Meadow-Endpoint-Count.js +49 -0
- package/source/endpoints/count/Meadow-Endpoint-CountBy.js +40 -0
- package/source/endpoints/create/Meadow-Endpoint-BulkCreate.js +53 -0
- package/source/endpoints/create/Meadow-Endpoint-Create.js +58 -0
- package/source/endpoints/create/Meadow-Operation-Create.js +83 -0
- package/source/endpoints/delete/Meadow-Endpoint-Delete.js +93 -0
- package/source/endpoints/delete/Meadow-Endpoint-Undelete.js +108 -0
- package/source/endpoints/read/Meadow-Endpoint-Read.js +72 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadDistinctList.js +92 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadLiteList.js +85 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadMax.js +55 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadSelectList.js +89 -0
- package/source/endpoints/read/Meadow-Endpoint-Reads.js +75 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadsBy.js +100 -0
- package/source/{crud → endpoints/read}/Meadow-Marshal-DistinctList.js +4 -13
- package/source/{crud → endpoints/read}/Meadow-Marshal-LiteList.js +5 -14
- package/source/endpoints/schema/Meadow-Endpoint-New.js +36 -0
- package/source/endpoints/schema/Meadow-Endpoint-Schema.js +36 -0
- package/source/endpoints/schema/Meadow-Endpoint-Validate.js +41 -0
- package/source/endpoints/update/Meadow-Endpoint-BulkUpdate.js +50 -0
- package/source/endpoints/update/Meadow-Endpoint-Update.js +58 -0
- package/source/endpoints/update/Meadow-Operation-Update.js +115 -0
- package/source/endpoints/upsert/Meadow-Endpoint-BulkUpsert.js +52 -0
- package/source/endpoints/upsert/Meadow-Endpoint-Upsert.js +57 -0
- package/source/endpoints/upsert/Meadow-Operation-Upsert.js +137 -0
- package/test/MeadowEndpoints_basic_tests.js +50 -2408
- package/test_support/bookstore-api-endpoint-exercises.paw +0 -0
- package/test_support/bookstore-configuration.json +28 -0
- package/test_support/bookstore-import-books-run.js +1 -0
- package/test_support/bookstore-import-books.js +215 -0
- package/test_support/bookstore-serve-meadow-endpoint-apis-IPC.js +138 -0
- package/test_support/bookstore-serve-meadow-endpoint-apis-run.js +6 -0
- package/test_support/bookstore-serve-meadow-endpoint-apis.js +129 -0
- package/test_support/data/books.csv +10001 -0
- package/test_support/model/ddl/BookStore.ddl +66 -0
- package/test_support/model/generated_diagram/README.md +1 -0
- package/test_support/model/generated_diagram/Stricture_Output.dot +13 -0
- package/test_support/model/generated_diagram/Stricture_Output.png +0 -0
- package/test_support/model/generated_documentation/Dictionary.md +18 -0
- package/test_support/model/generated_documentation/Model-Author.md +20 -0
- package/test_support/model/generated_documentation/Model-Book.md +26 -0
- package/test_support/model/generated_documentation/Model-BookAuthorJoin.md +14 -0
- package/test_support/model/generated_documentation/Model-BookPrice.md +25 -0
- package/test_support/model/generated_documentation/Model-Review.md +22 -0
- package/test_support/model/generated_documentation/ModelChangeTracking.md +17 -0
- package/test_support/model/generated_documentation/README.md +1 -0
- package/test_support/model/manual_scripts/DropTables.sql +5 -0
- package/test_support/model/manual_scripts/README.md +2 -0
- package/test_support/model/sql_create/BookStore-CreateDatabase.mysql.sql +116 -0
- package/test_support/model/sql_create/README.md +1 -0
- package/test_support/test_old/Tests.js +3243 -0
- package/test_support/test_old/untitled.js +88 -0
- package/source/Meadow-Authenticator.js +0 -31
- package/source/Meadow-Authorizers.js +0 -214
- package/source/Meadow-BehaviorModifications.js +0 -170
- package/source/Meadow-CommonServices.js +0 -206
- package/source/Meadow-MarshallSessionData.js +0 -64
- package/source/Restify-RouteParser.js +0 -114
- package/source/authorizers/Meadow-Authorizer-Allow.js +0 -17
- package/source/authorizers/Meadow-Authorizer-Deny.js +0 -17
- package/source/authorizers/Meadow-Authorizer-Mine.js +0 -47
- package/source/authorizers/Meadow-Authorizer-MyCustomer.js +0 -48
- package/source/crud/Meadow-Endpoint-BulkCreate.js +0 -67
- package/source/crud/Meadow-Endpoint-BulkUpdate.js +0 -74
- package/source/crud/Meadow-Endpoint-BulkUpsert.js +0 -76
- package/source/crud/Meadow-Endpoint-Count.js +0 -93
- package/source/crud/Meadow-Endpoint-CountBy.js +0 -101
- package/source/crud/Meadow-Endpoint-Create.js +0 -77
- package/source/crud/Meadow-Endpoint-Delete.js +0 -139
- package/source/crud/Meadow-Endpoint-Read.js +0 -109
- package/source/crud/Meadow-Endpoint-ReadDistinctList.js +0 -146
- package/source/crud/Meadow-Endpoint-ReadLiteList.js +0 -139
- package/source/crud/Meadow-Endpoint-ReadMax.js +0 -86
- package/source/crud/Meadow-Endpoint-ReadSelectList.js +0 -145
- package/source/crud/Meadow-Endpoint-Reads.js +0 -129
- package/source/crud/Meadow-Endpoint-ReadsBy.js +0 -155
- package/source/crud/Meadow-Endpoint-Undelete.js +0 -161
- package/source/crud/Meadow-Endpoint-Update.js +0 -80
- package/source/crud/Meadow-Endpoint-Upsert.js +0 -78
- package/source/crud/Meadow-Operation-Create.js +0 -105
- package/source/crud/Meadow-Operation-Update.js +0 -145
- package/source/crud/Meadow-Operation-Upsert.js +0 -106
- package/source/crud/Meadow-StreamRecordArray.js +0 -45
- package/source/schema/Meadow-Endpoint-New.js +0 -37
- package/source/schema/Meadow-Endpoint-Schema.js +0 -37
- package/source/schema/Meadow-Endpoint-Validate.js +0 -43
- package/test/Animal.json +0 -140
- package/test/MeadowEndpoints_disabledAuth_tests.js +0 -1325
- package/test/MeadowEndpoints_trustedSession_tests.js +0 -1731
- package/test/load/artillery-low.yml +0 -10
- package/test/load/cloud9setup.sh +0 -25
- package/test/load/package.json +0 -19
- package/test/load/test-schema-initializedatabase.sql +0 -29
- package/test/load/test-schema.json +0 -119
- package/test/load/test-server.js +0 -157
- package/test/scripts/InitializeDatabase-C9.sql +0 -7
- /package/{test/schemas → test_support/model}/json_schema/BookStore-Extended.json +0 -0
- /package/{test/schemas → test_support/model}/json_schema/BookStore-PICT.json +0 -0
- /package/{test/schemas → test_support/model}/json_schema/BookStore.json +0 -0
- /package/{test/schemas → test_support/model}/json_schema/README.md +0 -0
- /package/{test/schemas → test_support/model}/meadow_schema/BookStore-MeadowSchema-Author.json +0 -0
- /package/{test/schemas → test_support/model}/meadow_schema/BookStore-MeadowSchema-Book.json +0 -0
- /package/{test/schemas → test_support/model}/meadow_schema/BookStore-MeadowSchema-BookAuthorJoin.json +0 -0
- /package/{test/schemas → test_support/model}/meadow_schema/BookStore-MeadowSchema-BookPrice.json +0 -0
- /package/{test/schemas → test_support/model}/meadow_schema/BookStore-MeadowSchema-Review.json +0 -0
- /package/{test/schemas → test_support/model}/meadow_schema/README.md +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Endpoint Utility Class - Parse a Filter String and put it into a Query.
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
* @module Meadow
|
|
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
|
+
|
|
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
|
+
*/
|
|
46
|
+
|
|
47
|
+
class MeadowEndpointsFilterParser
|
|
48
|
+
{
|
|
49
|
+
constructor(pController)
|
|
50
|
+
{
|
|
51
|
+
this._Controller = pController;
|
|
52
|
+
}
|
|
53
|
+
|
|
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
|
+
|
|
170
|
+
parseFilter(pFilterString, pQuery)
|
|
171
|
+
{
|
|
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
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module.exports = MeadowEndpointsFilterParser;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class MeadowEndpointsSessionMarshaler
|
|
2
|
+
{
|
|
3
|
+
constructor(pController)
|
|
4
|
+
{
|
|
5
|
+
this._Controller = pController;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
getSessionData(pRequest)
|
|
9
|
+
{
|
|
10
|
+
let tmpSession = Object.assign({}, this._Controller.settings.MeadowEndpointsDefaultSessionObject);
|
|
11
|
+
|
|
12
|
+
switch (this._Controller.settings.MeadowEndpointsSessionDataSource || 'Request')
|
|
13
|
+
{
|
|
14
|
+
default:
|
|
15
|
+
this._LogController.warn(`Unknown session source configured: ${_SessionDataSource} - defaulting to Request for backward compatibility`);
|
|
16
|
+
case 'Request':
|
|
17
|
+
// noop - already set by orator-session
|
|
18
|
+
tmpSession = this._Controller.extend(tmpSession, pRequest.UserSession);
|
|
19
|
+
break;
|
|
20
|
+
case 'None':
|
|
21
|
+
break;
|
|
22
|
+
case 'Header':
|
|
23
|
+
try
|
|
24
|
+
{
|
|
25
|
+
const tmpHeaderSessionString = pRequest.headers['x-trusted-session'];
|
|
26
|
+
if (!tmpHeaderSessionString)
|
|
27
|
+
{
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
tmpHeaderSession = JSON.parse(tmpHeaderSessionString);
|
|
31
|
+
tmpSession = this._Controller.extend(tmpSession, pRequest.tmpHeaderSession);
|
|
32
|
+
}
|
|
33
|
+
catch (pError)
|
|
34
|
+
{
|
|
35
|
+
this._LogController.error(`Meadow Endpoints attempted to process a Header Session String with value [${tmpHeaderSessionString}] and failed -- likely culprit is bad JSON.`)
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Do we keep this here for backwards compatibility?
|
|
41
|
+
// Yes this makes sense here.
|
|
42
|
+
pRequest.UserSession = tmpSession;
|
|
43
|
+
|
|
44
|
+
return tmpSession;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = MeadowEndpointsSessionMarshaler;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Endpoint Streamer - Stream an array of recods as JSON to an output stream.
|
|
3
|
+
*/
|
|
4
|
+
const libAsyncEachSeries = require('async/eachSeries');
|
|
5
|
+
const JSONStream = require('JSONStream');
|
|
6
|
+
|
|
7
|
+
class MeadowEndpointsStreamRecordArray
|
|
8
|
+
{
|
|
9
|
+
constructor(pController)
|
|
10
|
+
{
|
|
11
|
+
this._Controller = pController;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
chunk(pInput, pChunkSize, pChunkCache)
|
|
15
|
+
{
|
|
16
|
+
let tmpInputArray = [...pInput];
|
|
17
|
+
// Note lodash defaults to 1, underscore defaults to 0
|
|
18
|
+
let tmpChunkSize = (typeof(pChunkSize) == 'number') ? pChunkSize : 0;
|
|
19
|
+
let tmpChunkCache = (typeof(pChunkCache) != 'undefined') ? pChunkCache : [];
|
|
20
|
+
|
|
21
|
+
if (tmpChunkSize <= 0)
|
|
22
|
+
{
|
|
23
|
+
return tmpChunkCache;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
while (tmpInputArray.length)
|
|
27
|
+
{
|
|
28
|
+
tmpChunkCache.push(tmpInputArray.splice(0, tmpChunkSize));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return tmpChunkCache;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
streamRecordArray(pResponse, pRecords, fCallback)
|
|
35
|
+
{
|
|
36
|
+
// for meadow invoke, writeHead isn't provided, so just call send(), which is the shim it uses...
|
|
37
|
+
// also, for small arrays, don't bother with the async serialization; this threshold could use tuning
|
|
38
|
+
if (!pResponse.writeHead || !Array.isArray(pRecords) || pRecords.length < 2500)
|
|
39
|
+
{
|
|
40
|
+
pResponse.send(pRecords);
|
|
41
|
+
return fCallback();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pResponse.writeHead(200,
|
|
45
|
+
{
|
|
46
|
+
'content-type': 'application/json',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const recordJsonMarshaller = JSONStream.stringify();
|
|
50
|
+
recordJsonMarshaller.pipe(pResponse);
|
|
51
|
+
|
|
52
|
+
// we write the records in chunks; doing one per loop is very inefficient, doing all is the same as not doing this at all
|
|
53
|
+
libAsyncEachSeries(this.chunk(pRecords, 1000), (pRecordChunk, fNext) =>
|
|
54
|
+
{
|
|
55
|
+
pRecordChunk.forEach(recordJsonMarshaller.write);
|
|
56
|
+
setImmediate(fNext);
|
|
57
|
+
},
|
|
58
|
+
(error) =>
|
|
59
|
+
{
|
|
60
|
+
recordJsonMarshaller.end();
|
|
61
|
+
fCallback(error);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = MeadowEndpointsStreamRecordArray;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Endpoint - Count a Record
|
|
3
|
+
*/
|
|
4
|
+
const doAPIEndpointCount = function(pRequest, pResponse, fNext)
|
|
5
|
+
{
|
|
6
|
+
let tmpRequestState = this.initializeRequestState(pRequest, 'Count');
|
|
7
|
+
let fBehaviorInjector = (pBehaviorHash) => { return (fStageComplete) => { this.BehaviorInjection.runBehavior(pBehaviorHash, this, pRequest, tmpRequestState, fStageComplete); }; };
|
|
8
|
+
|
|
9
|
+
this.waterfall(
|
|
10
|
+
[
|
|
11
|
+
(fStageComplete) =>
|
|
12
|
+
{
|
|
13
|
+
tmpRequestState.Query = this.DAL.query;
|
|
14
|
+
if (typeof(pRequest.params.Filter) === 'string')
|
|
15
|
+
{
|
|
16
|
+
// If a filter has been passed in, parse it and add the values to the query.
|
|
17
|
+
this.parseFilter(pRequest.params.Filter, tmpRequestState.Query);
|
|
18
|
+
}
|
|
19
|
+
else if (pRequest.params.Filter)
|
|
20
|
+
{
|
|
21
|
+
tmpRequestState.Query.setFilter(pRequest.params.Filter);
|
|
22
|
+
}
|
|
23
|
+
return fStageComplete();
|
|
24
|
+
},
|
|
25
|
+
fBehaviorInjector(`Count-QueryConfiguration`),
|
|
26
|
+
(fStageComplete) =>
|
|
27
|
+
{
|
|
28
|
+
this.DAL.doCount(tmpRequestState.Query,
|
|
29
|
+
(pError, pQuery, pCount) =>
|
|
30
|
+
{
|
|
31
|
+
tmpRequestState.Result = {Count:pCount};
|
|
32
|
+
return fStageComplete(pError);
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
(fStageComplete) =>
|
|
36
|
+
{
|
|
37
|
+
pResponse.send(tmpRequestState.Result);
|
|
38
|
+
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Delivered recordset count of ${tmpRequestState.Result.Count} for ${this.DAL.scope}.`);
|
|
39
|
+
return fStageComplete();
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
(pError) =>
|
|
43
|
+
{
|
|
44
|
+
return this.ErrorHandler.handleErrorIfSet(pRequest, tmpRequestState, pResponse, pError, fNext);
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
module.exports = doAPIEndpointCount;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Endpoint - Count a Record filtered by a single value
|
|
3
|
+
*/
|
|
4
|
+
const doAPIEndpointCountBy = function(pRequest, pResponse, fNext)
|
|
5
|
+
{
|
|
6
|
+
let tmpRequestState = this.initializeRequestState(pRequest, 'CountBy');
|
|
7
|
+
let fBehaviorInjector = (pBehaviorHash) => { return (fStageComplete) => { this.BehaviorInjection.runBehavior(pBehaviorHash, this, pRequest, tmpRequestState, fStageComplete); }; };
|
|
8
|
+
|
|
9
|
+
this.waterfall(
|
|
10
|
+
[
|
|
11
|
+
(fStageComplete) =>
|
|
12
|
+
{
|
|
13
|
+
tmpRequestState.Query = this.DAL.query;
|
|
14
|
+
tmpRequestState.Query.addFilter(pRequest.params.ByField, pRequest.params.ByValue, '=', 'AND', 'RequestByField');
|
|
15
|
+
return fStageComplete();
|
|
16
|
+
},
|
|
17
|
+
fBehaviorInjector(`CountBy-QueryConfiguration`),
|
|
18
|
+
(fStageComplete) =>
|
|
19
|
+
{
|
|
20
|
+
this.DAL.doCount(tmpRequestState.Query,
|
|
21
|
+
(pError, pQuery, pCount) =>
|
|
22
|
+
{
|
|
23
|
+
tmpRequestState.Result = {Count:pCount};
|
|
24
|
+
return fStageComplete(pError);
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
(fStageComplete) =>
|
|
28
|
+
{
|
|
29
|
+
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, 'Delivered recordset count of '+tmpRequestState.Result.Count+'.');
|
|
30
|
+
pResponse.send(tmpRequestState.Result);
|
|
31
|
+
return fStageComplete();
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
(pError) =>
|
|
35
|
+
{
|
|
36
|
+
return this.ErrorHandler.handleErrorIfSet(pRequest, tmpRequestState, pResponse, pError, fNext);
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
module.exports = doAPIEndpointCountBy;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Endpoint - Create a set of Record in Bulk
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const doCreate = require('./Meadow-Operation-Create.js');
|
|
6
|
+
|
|
7
|
+
const doAPIEndpointBulkCreate = function(pRequest, pResponse, fNext)
|
|
8
|
+
{
|
|
9
|
+
let tmpRequestState = this.initializeRequestState(pRequest, 'CreateBulk');
|
|
10
|
+
let fBehaviorInjector = (pBehaviorHash) => { return (fStageComplete) => { this.BehaviorInjection.runBehavior(pBehaviorHash, this, pRequest, tmpRequestState, fStageComplete); }; };
|
|
11
|
+
|
|
12
|
+
tmpRequestState.CreatedRecords = [];
|
|
13
|
+
|
|
14
|
+
this.waterfall(
|
|
15
|
+
[
|
|
16
|
+
(fStageComplete) =>
|
|
17
|
+
{
|
|
18
|
+
if (!Array.isArray(pRequest.body))
|
|
19
|
+
{
|
|
20
|
+
return fStageComplete(this.ErrorHandler.getError('Bulk record create failure - a valid array of records to create is required.', 500));
|
|
21
|
+
}
|
|
22
|
+
pRequest.RecordsToBulkCreate = pRequest.body;
|
|
23
|
+
|
|
24
|
+
return fStageComplete();
|
|
25
|
+
},
|
|
26
|
+
fBehaviorInjector(`CreateBulk-PreOperation`),
|
|
27
|
+
(fStageComplete) =>
|
|
28
|
+
{
|
|
29
|
+
// TODO: Research parallelism opportunities from custom routes
|
|
30
|
+
this.eachLimit(pRequest.RecordsToBulkCreate, 1,
|
|
31
|
+
(pRecord, fCallback) =>
|
|
32
|
+
{
|
|
33
|
+
doCreate.call(this, pRecord, pRequest, tmpRequestState, pResponse, fCallback);
|
|
34
|
+
}, fStageComplete);
|
|
35
|
+
},
|
|
36
|
+
fBehaviorInjector(`CreateBulk-PostOperation`),
|
|
37
|
+
(fStageComplete) =>
|
|
38
|
+
{
|
|
39
|
+
return this.doStreamRecordArray(pResponse, tmpRequestState.CreatedRecords, fStageComplete);
|
|
40
|
+
},
|
|
41
|
+
(fStageComplete) =>
|
|
42
|
+
{
|
|
43
|
+
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Bulk created ${tmpRequestState.CreatedRecords.length} records`);
|
|
44
|
+
return fStageComplete();
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
(pError) =>
|
|
48
|
+
{
|
|
49
|
+
return this.ErrorHandler.handleErrorIfSet(pRequest, tmpRequestState, pResponse, pError, fNext);
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
module.exports = doAPIEndpointBulkCreate;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Endpoint - Create a Record
|
|
3
|
+
*/
|
|
4
|
+
const doCreate = require('./Meadow-Operation-Create.js');
|
|
5
|
+
|
|
6
|
+
const doAPIEndpointCreate = function(pRequest, pResponse, fNext)
|
|
7
|
+
{
|
|
8
|
+
let tmpRequestState = this.initializeRequestState(pRequest, 'Create');
|
|
9
|
+
let fBehaviorInjector = (pBehaviorHash) => { return (fStageComplete) => { this.BehaviorInjection.runBehavior(pBehaviorHash, this, pRequest, tmpRequestState, fStageComplete); }; };
|
|
10
|
+
|
|
11
|
+
this.waterfall(
|
|
12
|
+
[
|
|
13
|
+
(fStageComplete) =>
|
|
14
|
+
{
|
|
15
|
+
if (typeof(pRequest.body) !== 'object')
|
|
16
|
+
{
|
|
17
|
+
return fStageComplete(this.ErrorHandler.getError('Record create failure - a valid record is required.', 500));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return fStageComplete();
|
|
21
|
+
},
|
|
22
|
+
(fStageComplete) =>
|
|
23
|
+
{
|
|
24
|
+
doCreate.call(this, pRequest.body, pRequest, tmpRequestState, pResponse, fStageComplete);
|
|
25
|
+
},
|
|
26
|
+
(fStageComplete) =>
|
|
27
|
+
{
|
|
28
|
+
if (tmpRequestState.RecordCreateError)
|
|
29
|
+
{
|
|
30
|
+
return fStageComplete(tmpRequestState.RecordCreateErrorObject);
|
|
31
|
+
}
|
|
32
|
+
if (tmpRequestState.CreatedRecords.length < 1)
|
|
33
|
+
{
|
|
34
|
+
return fStageComplete(this.ErrorHandler.getError('Unknown record create failure - no created records returned.', 500));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
tmpRequestState.Record = tmpRequestState.CreatedRecords[0];
|
|
38
|
+
|
|
39
|
+
return fStageComplete();
|
|
40
|
+
},
|
|
41
|
+
(fStageComplete) =>
|
|
42
|
+
{
|
|
43
|
+
pResponse.send(tmpRequestState.Record);
|
|
44
|
+
return fStageComplete();
|
|
45
|
+
},
|
|
46
|
+
(fStageComplete) =>
|
|
47
|
+
{
|
|
48
|
+
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Created a ${this.DAL.scope} record ID ${tmpRequestState.Record[this.DAL.defaultIdentifier]}`);
|
|
49
|
+
return fStageComplete();
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
(pError) =>
|
|
53
|
+
{
|
|
54
|
+
return this.ErrorHandler.handleErrorIfSet(pRequest, tmpRequestState, pResponse, pError, fNext);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = doAPIEndpointCreate;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Operation - Create a record function
|
|
3
|
+
*/
|
|
4
|
+
const doCreate = function(pRecord, pRequest, pRequestState, pResponse, fCallback)
|
|
5
|
+
{
|
|
6
|
+
// This is a virtual operation
|
|
7
|
+
let tmpRequestState = this.cloneAsyncSafeRequestState(pRequestState, 'doCreate');
|
|
8
|
+
let fBehaviorInjector = (pBehaviorHash) => { return (fStageComplete) => { this.BehaviorInjection.runBehavior(pBehaviorHash, this, pRequest, tmpRequestState, fStageComplete); }; };
|
|
9
|
+
|
|
10
|
+
if (!Array.isArray(tmpRequestState.ParentRequestState.CreatedRecords))
|
|
11
|
+
{
|
|
12
|
+
tmpRequestState.ParentRequestState.CreatedRecords = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
this.waterfall(
|
|
16
|
+
[
|
|
17
|
+
(fStageComplete) =>
|
|
18
|
+
{
|
|
19
|
+
tmpRequestState.RecordToCreate = pRecord;
|
|
20
|
+
|
|
21
|
+
//Make sure record gets created with a customerID
|
|
22
|
+
if (!tmpRequestState.RecordToCreate.hasOwnProperty('IDCustomer') && this.DAL.jsonSchema.properties.hasOwnProperty('IDCustomer'))
|
|
23
|
+
{
|
|
24
|
+
tmpRequestState.RecordToCreate.IDCustomer = tmpRequestState.SessionData.CustomerID || 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return fStageComplete();
|
|
28
|
+
},
|
|
29
|
+
(fStageComplete) => { this.BehaviorInjection.runBehavior(`Create-PreOperation`, this, pRequest, tmpRequestState, fStageComplete); },
|
|
30
|
+
(fStageComplete) =>
|
|
31
|
+
{
|
|
32
|
+
// Prepare create query
|
|
33
|
+
tmpRequestState.Query = this.DAL.query;
|
|
34
|
+
tmpRequestState.Query.setIDUser(tmpRequestState.SessionData.UserID);
|
|
35
|
+
tmpRequestState.Query.addRecord(tmpRequestState.RecordToCreate);
|
|
36
|
+
return fStageComplete();
|
|
37
|
+
},
|
|
38
|
+
(fStageComplete) => { this.BehaviorInjection.runBehavior(`Create-QueryConfiguration`, this, pRequest, tmpRequestState, fStageComplete); },
|
|
39
|
+
(fStageComplete) =>
|
|
40
|
+
{
|
|
41
|
+
// Do the actual create operation with the DAL
|
|
42
|
+
this.DAL.doCreate(tmpRequestState.Query,
|
|
43
|
+
(pError, pQuery, pReadQuery, pNewRecord) =>
|
|
44
|
+
{
|
|
45
|
+
if (pError)
|
|
46
|
+
{
|
|
47
|
+
return fStageComplete(pError);
|
|
48
|
+
}
|
|
49
|
+
if (!pNewRecord)
|
|
50
|
+
{
|
|
51
|
+
return fStageComplete(this.ErrorHandler.getError(`Error in DAL Create: No record returned from persistence engine.`, 500));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
tmpRequestState.Record = pNewRecord;
|
|
55
|
+
|
|
56
|
+
return fStageComplete();
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
(fStageComplete) => { return this.BehaviorInjection.runBehavior(`Create-PostOperation`, this, pRequest, tmpRequestState, fStageComplete); },
|
|
60
|
+
(fStageComplete) =>
|
|
61
|
+
{
|
|
62
|
+
tmpRequestState.ParentRequestState.CreatedRecords.push(tmpRequestState.Record);
|
|
63
|
+
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Created a record with ${this.DAL.defaultIdentifier} = ${tmpRequestState.Record[this.DAL.defaultIdentifier]}`);
|
|
64
|
+
return fStageComplete();
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
(pError) =>
|
|
68
|
+
{
|
|
69
|
+
if (pError)
|
|
70
|
+
{
|
|
71
|
+
tmpRequestState.RecordToCreate.Error = pError;
|
|
72
|
+
|
|
73
|
+
tmpRequestState.ParentRequestState.RecordCreateError = true;
|
|
74
|
+
tmpRequestState.ParentRequestState.RecordCreateErrorObject = pError;
|
|
75
|
+
|
|
76
|
+
tmpRequestState.ParentRequestState.CreatedRecords.push(tmpRequestState.RecordToCreate);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return fCallback();
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
module.exports = doCreate;
|