meadow-endpoints 4.0.23 → 4.0.24
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/meadow-endpoints.js +250 -217
- package/dist/meadow-endpoints.js.map +1 -1
- package/dist/meadow-endpoints.min.js +18 -18
- package/dist/meadow-endpoints.min.js.map +1 -1
- package/package.json +1 -1
- package/source/Meadow-Endpoints.js +4 -0
- package/source/endpoints/read/Meadow-Endpoint-Query.js +86 -0
- package/test/MeadowEndpoints_basic_tests.js +97 -0
package/package.json
CHANGED
|
@@ -73,6 +73,9 @@ class MeadowEndpoints
|
|
|
73
73
|
Reads: require('./endpoints/read/Meadow-Endpoint-Reads.js'),
|
|
74
74
|
ReadsBy: require('./endpoints/read/Meadow-Endpoint-ReadsBy.js'),
|
|
75
75
|
|
|
76
|
+
// Body-driven read: filter/pagination/mode travel in a JSON POST body
|
|
77
|
+
Query: require('./endpoints/read/Meadow-Endpoint-Query.js'),
|
|
78
|
+
|
|
76
79
|
ReadSelectList: require('./endpoints/read/Meadow-Endpoint-ReadSelectList.js'),
|
|
77
80
|
ReadLiteList: require('./endpoints/read/Meadow-Endpoint-ReadLiteList.js'),
|
|
78
81
|
ReadDistinctList: require('./endpoints/read/Meadow-Endpoint-ReadDistinctList.js'),
|
|
@@ -183,6 +186,7 @@ class MeadowEndpoints
|
|
|
183
186
|
}
|
|
184
187
|
if (this._EnabledBehaviorSets.Reads)
|
|
185
188
|
{
|
|
189
|
+
this.connectRoute(pServiceServer, 'postWithBodyParser', `s/Query`, this._Endpoints.Query, `the internal behavior _Endpoints.Query`);
|
|
186
190
|
this.connectRoute(pServiceServer, 'get', `s`, this._Endpoints.Reads, `the internal behavior _Endpoints.Reads`);
|
|
187
191
|
this.connectRoute(pServiceServer, 'get', `s/By/:ByField/:ByValue`, this._Endpoints.ReadsBy, `the internal behavior _Endpoints.ReadsBy`);
|
|
188
192
|
this.connectRoute(pServiceServer, 'get', `s/By/:ByField/:ByValue/:Begin/:Cap`, this._Endpoints.ReadsBy, `the internal behavior _Endpoints.ReadsBy`);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meadow Endpoint - Body-driven Read
|
|
3
|
+
*
|
|
4
|
+
* A single POST endpoint that carries the filter, pagination and read-mode
|
|
5
|
+
* selection in a JSON body instead of the URI. This sidesteps URI length
|
|
6
|
+
* limits hit by complex filters and large IN-lists, while reusing the exact
|
|
7
|
+
* GET read handlers (and therefore their behavior hooks, marshalling and
|
|
8
|
+
* response shapes) by mapping the body onto pRequest.params and delegating.
|
|
9
|
+
*
|
|
10
|
+
* Body envelope:
|
|
11
|
+
* {
|
|
12
|
+
* "Filter": "FBV~Genre~EQ~Books~...", // meadow-filter string
|
|
13
|
+
* "Begin": 0,
|
|
14
|
+
* "Cap": 250,
|
|
15
|
+
* "ExtraColumns": "ColumnA,ColumnB", // Lite read
|
|
16
|
+
* "Columns": "ColumnA,ColumnB", // Distinct read
|
|
17
|
+
* "Lite": true, "Distinct": true, "Count": true
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* The read mode is selected by the flags, resolved by precedence:
|
|
21
|
+
* Count > Distinct > Lite > Reads (the default). This lets a caller compose a
|
|
22
|
+
* read query (filter, pagination, Lite/Distinct shaping) and flip Count on to
|
|
23
|
+
* get the count of that same query.
|
|
24
|
+
*/
|
|
25
|
+
const doReads = require('./Meadow-Endpoint-Reads.js');
|
|
26
|
+
const doReadLite = require('./Meadow-Endpoint-ReadLiteList.js');
|
|
27
|
+
const doReadDistinct = require('./Meadow-Endpoint-ReadDistinctList.js');
|
|
28
|
+
const doCount = require('../count/Meadow-Endpoint-Count.js');
|
|
29
|
+
|
|
30
|
+
// Body keys hydrated onto pRequest.params so the delegated GET handlers see
|
|
31
|
+
// the same inputs they read from the URI.
|
|
32
|
+
const PARAM_KEYS = [ 'Filter', 'Begin', 'Cap', 'ExtraColumns', 'Columns' ];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the read mode from the request body flags, by precedence:
|
|
36
|
+
* Count > Distinct > Lite > Reads (the default).
|
|
37
|
+
*
|
|
38
|
+
* @param {Record<string, any>} pBody - the parsed request body
|
|
39
|
+
*
|
|
40
|
+
* @return {string} one of 'Count', 'Distinct', 'Lite', 'Reads'
|
|
41
|
+
*/
|
|
42
|
+
const resolveMode = function(pBody)
|
|
43
|
+
{
|
|
44
|
+
if (pBody.Count)
|
|
45
|
+
{
|
|
46
|
+
return 'Count';
|
|
47
|
+
}
|
|
48
|
+
if (pBody.Distinct)
|
|
49
|
+
{
|
|
50
|
+
return 'Distinct';
|
|
51
|
+
}
|
|
52
|
+
if (pBody.Lite)
|
|
53
|
+
{
|
|
54
|
+
return 'Lite';
|
|
55
|
+
}
|
|
56
|
+
return 'Reads';
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const doAPIEndpointQuery = function(pRequest, pResponse, fNext)
|
|
60
|
+
{
|
|
61
|
+
const tmpBody = (pRequest.body && typeof(pRequest.body) === 'object') ? pRequest.body : {};
|
|
62
|
+
pRequest.params = (pRequest.params && typeof(pRequest.params) === 'object') ? pRequest.params : {};
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < PARAM_KEYS.length; i++)
|
|
65
|
+
{
|
|
66
|
+
if (typeof(tmpBody[PARAM_KEYS[i]]) !== 'undefined')
|
|
67
|
+
{
|
|
68
|
+
pRequest.params[PARAM_KEYS[i]] = tmpBody[PARAM_KEYS[i]];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
switch (resolveMode(tmpBody))
|
|
73
|
+
{
|
|
74
|
+
case 'Count':
|
|
75
|
+
return doCount.call(this, pRequest, pResponse, fNext);
|
|
76
|
+
case 'Distinct':
|
|
77
|
+
return doReadDistinct.call(this, pRequest, pResponse, fNext);
|
|
78
|
+
case 'Lite':
|
|
79
|
+
return doReadLite.call(this, pRequest, pResponse, fNext);
|
|
80
|
+
case 'Reads':
|
|
81
|
+
default:
|
|
82
|
+
return doReads.call(this, pRequest, pResponse, fNext);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
module.exports = doAPIEndpointQuery;
|
|
@@ -505,6 +505,103 @@ suite
|
|
|
505
505
|
}
|
|
506
506
|
);
|
|
507
507
|
test
|
|
508
|
+
(
|
|
509
|
+
"query: reads all records via JSON body (default mode)",
|
|
510
|
+
function (fDone)
|
|
511
|
+
{
|
|
512
|
+
_SuperTest
|
|
513
|
+
.post("1.0/Books/Query")
|
|
514
|
+
.send({})
|
|
515
|
+
.end(
|
|
516
|
+
(pError, pResponse) =>
|
|
517
|
+
{
|
|
518
|
+
Expect(pError).to.not.exist;
|
|
519
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
520
|
+
Expect(tmpResult).to.be.an("array");
|
|
521
|
+
Expect(tmpResult.length).to.be.at.least(5);
|
|
522
|
+
fDone();
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
);
|
|
527
|
+
test
|
|
528
|
+
(
|
|
529
|
+
"query: reads with filter and pagination in the body",
|
|
530
|
+
function (fDone)
|
|
531
|
+
{
|
|
532
|
+
_SuperTest
|
|
533
|
+
.post("1.0/Books/Query")
|
|
534
|
+
.send({ Filter: "FBV~Genre~LK~%Science%", Begin: 0, Cap: 1 })
|
|
535
|
+
.end(
|
|
536
|
+
(pError, pResponse) =>
|
|
537
|
+
{
|
|
538
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
539
|
+
Expect(tmpResult).to.be.an("array");
|
|
540
|
+
Expect(tmpResult.length).to.equal(1);
|
|
541
|
+
Expect(tmpResult[0].Genre).to.contain("Science");
|
|
542
|
+
fDone();
|
|
543
|
+
}
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
);
|
|
547
|
+
test
|
|
548
|
+
(
|
|
549
|
+
"query: Lite read via boolean flag",
|
|
550
|
+
function (fDone)
|
|
551
|
+
{
|
|
552
|
+
_SuperTest
|
|
553
|
+
.post("1.0/Books/Query")
|
|
554
|
+
.send({ Lite: true, Begin: 0, Cap: 3 })
|
|
555
|
+
.end(
|
|
556
|
+
(pError, pResponse) =>
|
|
557
|
+
{
|
|
558
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
559
|
+
Expect(tmpResult).to.be.an("array");
|
|
560
|
+
Expect(tmpResult.length).to.equal(3);
|
|
561
|
+
fDone();
|
|
562
|
+
}
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
test
|
|
567
|
+
(
|
|
568
|
+
"query: Distinct read via boolean flag and Columns",
|
|
569
|
+
function (fDone)
|
|
570
|
+
{
|
|
571
|
+
_SuperTest
|
|
572
|
+
.post("1.0/Books/Query")
|
|
573
|
+
.send({ Distinct: true, Columns: "Genre" })
|
|
574
|
+
.end(
|
|
575
|
+
(pError, pResponse) =>
|
|
576
|
+
{
|
|
577
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
578
|
+
Expect(tmpResult).to.be.an("array");
|
|
579
|
+
Expect(tmpResult.length).to.be.at.least(1);
|
|
580
|
+
fDone();
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
);
|
|
585
|
+
test
|
|
586
|
+
(
|
|
587
|
+
"query: Count flag takes precedence and honors the filter",
|
|
588
|
+
function (fDone)
|
|
589
|
+
{
|
|
590
|
+
_SuperTest
|
|
591
|
+
.post("1.0/Books/Query")
|
|
592
|
+
.send({ Lite: true, Count: true, Filter: "FBV~Genre~LK~%Science%" })
|
|
593
|
+
.end(
|
|
594
|
+
(pError, pResponse) =>
|
|
595
|
+
{
|
|
596
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
597
|
+
Expect(tmpResult).to.have.property("Count");
|
|
598
|
+
Expect(tmpResult.Count).to.be.at.least(3);
|
|
599
|
+
fDone();
|
|
600
|
+
}
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
);
|
|
604
|
+
test
|
|
508
605
|
(
|
|
509
606
|
'reads by: get records filtered by a field value',
|
|
510
607
|
function (fDone)
|