meadow-endpoints 4.0.15 → 4.0.17
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/dist/indoctrinate_content_staging/Indoctrinate-Catalog-AppData.json +1286 -1065
- package/dist/meadow-endpoints.js +285 -148
- package/dist/meadow-endpoints.js.map +1 -1
- package/dist/meadow-endpoints.min.js +7 -7
- package/dist/meadow-endpoints.min.js.map +1 -1
- package/docs/_version.json +7 -0
- package/docs/css/docuserve.css +277 -23
- package/docs/index.html +2 -2
- package/docs/retold-catalog.json +13 -1
- package/docs/retold-keyword-index.json +1 -1
- package/package.json +8 -7
- package/source/Meadow-Endpoints-Browser-Shim.js +4 -1
- package/source/Meadow-Endpoints.js +6 -6
- package/source/controller/Meadow-Endpoints-Controller-Base.js +6 -6
- package/source/controller/components/Meadow-Endpoints-Controller-BehaviorInjection.js +11 -11
- package/source/controller/components/Meadow-Endpoints-Controller-Error.js +23 -15
- package/source/controller/components/Meadow-Endpoints-Controller-Log.js +9 -9
- package/source/controller/utility/Meadow-Endpoints-Filter-Parser.js +16 -16
- package/source/controller/utility/Meadow-Endpoints-Session-Marshaler.js +42 -42
- package/source/controller/utility/Meadow-Endpoints-Stream-RecordArray.js +6 -6
- package/source/endpoints/create/Meadow-Endpoint-BulkCreate.js +6 -0
- package/source/endpoints/create/Meadow-Endpoint-Create.js +8 -0
- package/source/endpoints/create/Meadow-Operation-Create.js +1 -1
- package/source/endpoints/delete/Meadow-Endpoint-Delete.js +6 -0
- package/source/endpoints/delete/Meadow-Endpoint-Undelete.js +5 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadDistinctList.js +7 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadLiteList.js +6 -0
- package/source/endpoints/read/Meadow-Endpoint-ReadSelectList.js +6 -0
- package/source/endpoints/schema/Meadow-Endpoint-Validate.js +1 -1
- package/source/endpoints/update/Meadow-Endpoint-BulkUpdate.js +2 -0
- package/source/endpoints/update/Meadow-Operation-Update.js +10 -0
- package/source/endpoints/upsert/Meadow-Endpoint-BulkUpsert.js +3 -1
- package/test/MeadowEndpoints_basic_tests.js +1069 -0
|
@@ -11,24 +11,24 @@ const { parse } = require('meadow-filter');
|
|
|
11
11
|
|
|
12
12
|
class MeadowEndpointsFilterParser
|
|
13
13
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @param {import('../Meadow-Endpoints-Controller-Base.js')} pController - The controller instance to which this parser belongs.
|
|
16
|
+
*/
|
|
17
|
+
constructor(pController)
|
|
18
18
|
{
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
this._Controller = pController;
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
+
*/
|
|
28
|
+
parseFilter(pFilterString, pQuery)
|
|
29
|
+
{
|
|
30
|
+
return parse(pFilterString, pQuery);
|
|
31
|
+
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
module.exports = MeadowEndpointsFilterParser;
|
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
class MeadowEndpointsSessionMarshaler
|
|
2
2
|
{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @param {import('../Meadow-Endpoints-Controller-Base.js')} pController
|
|
5
|
+
*/
|
|
6
|
+
constructor(pController)
|
|
7
7
|
{
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
this._Controller = pController;
|
|
9
|
+
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
getSessionData(pRequest)
|
|
12
|
+
{
|
|
13
|
+
let tmpSession = Object.assign({}, this._Controller.settings.MeadowEndpointsDefaultSessionObject);
|
|
14
|
+
let tmpHeaderSessionString;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
16
|
+
switch (this._Controller.settings.MeadowEndpointsSessionDataSource || 'Request')
|
|
17
|
+
{
|
|
18
|
+
default:
|
|
19
|
+
this._Controller.log.warn(`Unknown session source configured: ${this._Controller.settings.MeadowEndpointsSessionDataSource} - defaulting to Request for backward compatibility`);
|
|
20
|
+
case 'Request':
|
|
21
|
+
// noop - already set by orator-session
|
|
22
|
+
tmpSession = this._Controller.extend(tmpSession, pRequest.UserSession);
|
|
23
|
+
break;
|
|
24
|
+
case 'None':
|
|
25
|
+
break;
|
|
26
|
+
case 'Header':
|
|
27
|
+
try
|
|
28
|
+
{
|
|
29
|
+
tmpHeaderSessionString = pRequest.headers['x-trusted-session'];
|
|
30
|
+
if (!tmpHeaderSessionString)
|
|
31
|
+
{
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
const tmpHeaderSession = JSON.parse(tmpHeaderSessionString);
|
|
35
|
+
tmpSession = this._Controller.extend(tmpSession, tmpHeaderSession);
|
|
36
|
+
}
|
|
37
|
+
catch (pError)
|
|
38
|
+
{
|
|
39
|
+
this._Controller.log.error(`Meadow Endpoints attempted to process a Header Session String with value [${tmpHeaderSessionString}] and failed -- likely culprit is bad JSON.`)
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
// Do we keep this here for backwards compatibility?
|
|
45
|
+
// Yes this makes sense here.
|
|
46
|
+
pRequest.UserSession = tmpSession;
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
return tmpSession;
|
|
49
|
+
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
module.exports = MeadowEndpointsSessionMarshaler;
|
|
@@ -6,13 +6,13 @@ const JSONStream = require('JSONStream');
|
|
|
6
6
|
|
|
7
7
|
class MeadowEndpointsStreamRecordArray
|
|
8
8
|
{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('../Meadow-Endpoints-Controller-Base.js')} pController
|
|
11
|
+
*/
|
|
12
|
+
constructor(pController)
|
|
13
13
|
{
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
this._Controller = pController;
|
|
15
|
+
}
|
|
16
16
|
|
|
17
17
|
chunk(pInput, pChunkSize, pChunkCache)
|
|
18
18
|
{
|
|
@@ -23,6 +23,12 @@ const doAPIEndpointBulkCreate = function(pRequest, pResponse, fNext)
|
|
|
23
23
|
|
|
24
24
|
return fStageComplete();
|
|
25
25
|
},
|
|
26
|
+
// Endpoint-level pre-request hook for bulk creates. Mirror of
|
|
27
|
+
// Create-PreRequest in the singular Create endpoint — fires
|
|
28
|
+
// after body-array validation and before any per-record
|
|
29
|
+
// operation. Use cases include bulk idempotency suppression /
|
|
30
|
+
// dedup across the incoming batch.
|
|
31
|
+
fBehaviorInjector(`CreateBulk-PreRequest`),
|
|
26
32
|
fBehaviorInjector(`CreateBulk-PreOperation`),
|
|
27
33
|
(fStageComplete) =>
|
|
28
34
|
{
|
|
@@ -19,6 +19,14 @@ const doAPIEndpointCreate = function(pRequest, pResponse, fNext)
|
|
|
19
19
|
|
|
20
20
|
return fStageComplete();
|
|
21
21
|
},
|
|
22
|
+
// Endpoint-level pre-request hook. Runs after the body-type check
|
|
23
|
+
// but before any operation work, mirroring ME 2.x's
|
|
24
|
+
// Create-PreRequest stage. Use cases include idempotency
|
|
25
|
+
// suppression (e.g. look up by primary GUID and short-circuit
|
|
26
|
+
// if the row already exists). Handlers can abort the operation
|
|
27
|
+
// by calling fStageComplete with a truthy error or by fully
|
|
28
|
+
// writing pResponse and returning a sentinel.
|
|
29
|
+
fBehaviorInjector(`Create-PreRequest`),
|
|
22
30
|
(fStageComplete) =>
|
|
23
31
|
{
|
|
24
32
|
doCreate.call(this, pRequest.body, pRequest, tmpRequestState, pResponse, fStageComplete);
|
|
@@ -19,7 +19,7 @@ const doCreate = function(pRecord, pRequest, pRequestState, pResponse, fCallback
|
|
|
19
19
|
tmpRequestState.RecordToCreate = pRecord;
|
|
20
20
|
|
|
21
21
|
//Make sure record gets created with a customerID
|
|
22
|
-
if (!tmpRequestState.RecordToCreate.hasOwnProperty('IDCustomer') && this.DAL.jsonSchema.properties.hasOwnProperty('IDCustomer'))
|
|
22
|
+
if (!tmpRequestState.RecordToCreate.hasOwnProperty('IDCustomer') && this.DAL.jsonSchema && this.DAL.jsonSchema.properties && this.DAL.jsonSchema.properties.hasOwnProperty('IDCustomer'))
|
|
23
23
|
{
|
|
24
24
|
tmpRequestState.RecordToCreate.IDCustomer = tmpRequestState.SessionData.CustomerID || 0;
|
|
25
25
|
}
|
|
@@ -55,6 +55,12 @@ const doAPIEndpointDelete = function(pRequest, pResponse, fNext)
|
|
|
55
55
|
return fStageComplete(this.ErrorHandler.getError('Record not found.', 404));
|
|
56
56
|
}
|
|
57
57
|
tmpRequestState.Record = pRecord;
|
|
58
|
+
// Alias the loaded pre-delete row for symmetry
|
|
59
|
+
// with Update (see Meadow-Operation-Update.js).
|
|
60
|
+
// Post-op hooks that compare pre/post values can
|
|
61
|
+
// reliably read OriginalRecord without having to
|
|
62
|
+
// know which stage overwrote Record.
|
|
63
|
+
tmpRequestState.OriginalRecord = pRecord;
|
|
58
64
|
return fStageComplete();
|
|
59
65
|
});
|
|
60
66
|
},
|
|
@@ -70,6 +70,11 @@ const doAPIEndpointUndelete = function(pRequest, pResponse, fNext)
|
|
|
70
70
|
return fStageComplete(this.ErrorHandler.getError('Record not found.', 404));
|
|
71
71
|
}
|
|
72
72
|
tmpRequestState.Record = pRecord;
|
|
73
|
+
// Alias the loaded pre-undelete row for symmetry
|
|
74
|
+
// with Update / Delete (see their endpoints). Post-op
|
|
75
|
+
// hooks that compare pre/post values can reliably
|
|
76
|
+
// read OriginalRecord.
|
|
77
|
+
tmpRequestState.OriginalRecord = pRecord;
|
|
73
78
|
return fStageComplete();
|
|
74
79
|
});
|
|
75
80
|
},
|
|
@@ -70,6 +70,13 @@ const doAPIEndpointReadDistinct = function(pRequest, pResponse, fNext)
|
|
|
70
70
|
tmpRequestState.Records = pRecords;
|
|
71
71
|
return fStageComplete();
|
|
72
72
|
},
|
|
73
|
+
// Stage-specific post-op hook. Fires after DAL read but BEFORE
|
|
74
|
+
// the records are projected to distinct-column shape, so
|
|
75
|
+
// handlers can run against full rows. Separate from
|
|
76
|
+
// Reads-PostOperation so registering one doesn't unintentionally
|
|
77
|
+
// fire on the other. Hash matches the endpoint's action label
|
|
78
|
+
// (initializeRequestState(..., 'ReadDistinct')).
|
|
79
|
+
fBehaviorInjector(`ReadDistinct-PostOperation`),
|
|
73
80
|
(fStageComplete) =>
|
|
74
81
|
{
|
|
75
82
|
tmpRequestState.ResultRecords = marshalDistinctList.call(this, tmpRequestState.Records, pRequest, tmpRequestState.DistinctColumns);
|
|
@@ -60,8 +60,14 @@ const doAPIEndpointReadLite = function(pRequest, pResponse, fNext)
|
|
|
60
60
|
pRecords = [];
|
|
61
61
|
}
|
|
62
62
|
tmpRequestState.RawRecords = pRecords;
|
|
63
|
+
// Expose the loaded records under pRequestState.Records
|
|
64
|
+
// so post-op hooks operate on the same shape regular
|
|
65
|
+
// Reads uses. Marshalling to lite shape runs AFTER the
|
|
66
|
+
// hook so hooks see full rows.
|
|
67
|
+
tmpRequestState.Records = pRecords;
|
|
63
68
|
return fStageComplete();
|
|
64
69
|
},
|
|
70
|
+
fBehaviorInjector(`ReadsLite-PostOperation`),
|
|
65
71
|
(fStageComplete) =>
|
|
66
72
|
{
|
|
67
73
|
tmpRequestState.Records = marshalLiteList.call(this, tmpRequestState.RawRecords, pRequest, (typeof(pRequest.params.ExtraColumns) === 'string') ? pRequest.params.ExtraColumns.split(',') : []);
|
|
@@ -54,6 +54,12 @@ const doAPIEndpointReadSelectList = function(pRequest, pResponse, fNext)
|
|
|
54
54
|
|
|
55
55
|
return fStageComplete();
|
|
56
56
|
},
|
|
57
|
+
// Stage-specific post-op hook. Fires after DAL read but
|
|
58
|
+
// BEFORE the records are projected to select-list
|
|
59
|
+
// (Hash/Value) shape, so handlers can run against full
|
|
60
|
+
// rows. Separate from Reads-PostOperation so registering
|
|
61
|
+
// one doesn't unintentionally fire on the other.
|
|
62
|
+
fBehaviorInjector(`ReadSelectList-PostOperation`),
|
|
57
63
|
(fStageComplete) =>
|
|
58
64
|
{
|
|
59
65
|
tmpRequestState.SelectList = [];
|
|
@@ -27,7 +27,7 @@ const doAPIEndpointValidate = function(pRequest, pResponse, fNext)
|
|
|
27
27
|
(fStageComplete) =>
|
|
28
28
|
{
|
|
29
29
|
pResponse.send(tmpRequestState.RecordValidation);
|
|
30
|
-
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Validated Record for ${this.DAL.scope} - ${tmpRequestState.RecordValidation}`);
|
|
30
|
+
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Validated Record for ${this.DAL.scope} - ${JSON.stringify(tmpRequestState.RecordValidation)}`);
|
|
31
31
|
return fStageComplete();
|
|
32
32
|
}
|
|
33
33
|
],
|
|
@@ -23,6 +23,7 @@ const doAPIEndpointUpdate = function(pRequest, pResponse, fNext)
|
|
|
23
23
|
|
|
24
24
|
return fStageComplete();
|
|
25
25
|
},
|
|
26
|
+
fBehaviorInjector(`UpdateBulk-PreOperation`),
|
|
26
27
|
(fStageComplete) =>
|
|
27
28
|
{
|
|
28
29
|
this.eachLimit(tmpRequestState.BulkRecords, 1,
|
|
@@ -31,6 +32,7 @@ const doAPIEndpointUpdate = function(pRequest, pResponse, fNext)
|
|
|
31
32
|
doUpdate.call(this, pRecord, pRequest, tmpRequestState, pResponse, fCallback);
|
|
32
33
|
}, fStageComplete);
|
|
33
34
|
},
|
|
35
|
+
fBehaviorInjector(`UpdateBulk-PostOperation`),
|
|
34
36
|
(fStageComplete) =>
|
|
35
37
|
{
|
|
36
38
|
return this.doStreamRecordArray(pResponse, tmpRequestState.UpdatedRecords, fStageComplete);
|
|
@@ -49,6 +49,14 @@ const doUpdate = function(pRecordToModify, pRequest, pRequestState, pResponse, f
|
|
|
49
49
|
return fStageComplete(this.ErrorHandler.getError('Record not Found', 404));
|
|
50
50
|
}
|
|
51
51
|
tmpRequestState.Record = pRecord;
|
|
52
|
+
// Alias the loaded pre-update row under an
|
|
53
|
+
// unambiguous name. pRequestState.Record gets
|
|
54
|
+
// overwritten with the POST-update row later in
|
|
55
|
+
// this waterfall; OriginalRecord preserves the
|
|
56
|
+
// PRE-update reference for post-op hooks that
|
|
57
|
+
// need to compare before/after values (change
|
|
58
|
+
// logs, customer-boundary checks, etc.).
|
|
59
|
+
tmpRequestState.OriginalRecord = pRecord;
|
|
52
60
|
return fStageComplete();
|
|
53
61
|
});
|
|
54
62
|
}
|
|
@@ -58,6 +66,7 @@ const doUpdate = function(pRecordToModify, pRequest, pRequestState, pResponse, f
|
|
|
58
66
|
tmpRequestState.Query = this.DAL.query;
|
|
59
67
|
return fStageComplete();
|
|
60
68
|
},
|
|
69
|
+
fBehaviorInjector(`Update-PreOperation`),
|
|
61
70
|
(fStageComplete) =>
|
|
62
71
|
{
|
|
63
72
|
tmpRequestState.Query.setIDUser(tmpRequestState.SessionData.UserID);
|
|
@@ -65,6 +74,7 @@ const doUpdate = function(pRecordToModify, pRequest, pRequestState, pResponse, f
|
|
|
65
74
|
|
|
66
75
|
return fStageComplete();
|
|
67
76
|
},
|
|
77
|
+
fBehaviorInjector(`Update-QueryConfiguration`),
|
|
68
78
|
(fStageComplete) =>
|
|
69
79
|
{
|
|
70
80
|
this.DAL.doUpdate(tmpRequestState.Query,
|
|
@@ -28,6 +28,7 @@ const doAPIEndpointUpserts = function(pRequest, pResponse, fNext)
|
|
|
28
28
|
|
|
29
29
|
return fStageComplete();
|
|
30
30
|
},
|
|
31
|
+
fBehaviorInjector(`UpsertBulk-PreOperation`),
|
|
31
32
|
(fStageComplete) =>
|
|
32
33
|
{
|
|
33
34
|
this.eachLimit(tmpRequestState.BulkRecords, 1,
|
|
@@ -36,13 +37,14 @@ const doAPIEndpointUpserts = function(pRequest, pResponse, fNext)
|
|
|
36
37
|
doUpsert.call(this, pRecord, pRequest, tmpRequestState, pResponse, fCallback);
|
|
37
38
|
}, fStageComplete);
|
|
38
39
|
},
|
|
40
|
+
fBehaviorInjector(`UpsertBulk-PostOperation`),
|
|
39
41
|
(fStageComplete) =>
|
|
40
42
|
{
|
|
41
43
|
return this.doStreamRecordArray(pResponse, marshalLiteList.call(this, tmpRequestState.UpsertedRecords, pRequest), fStageComplete);
|
|
42
44
|
},
|
|
43
45
|
(fStageComplete) =>
|
|
44
46
|
{
|
|
45
|
-
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Bulk upsert complete -- ${tmpRequestState.UpsertedRecords} records processed`);
|
|
47
|
+
this.log.requestCompletedSuccessfully(pRequest, tmpRequestState, `Bulk upsert complete -- ${tmpRequestState.UpsertedRecords.length} records processed`);
|
|
46
48
|
return fStageComplete();
|
|
47
49
|
}
|
|
48
50
|
], (pError) =>
|