@sera4/essentia 1.0.6 → 1.0.7
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/README.md +8 -0
- package/hal/REAMDE.md +54 -0
- package/hal/index.js +74 -0
- package/index.js +3 -1
- package/package.json +1 -1
- package/package.tar.gz +0 -0
- package/paginator/README.md +183 -0
- package/paginator/sql-pagination.js +169 -0
package/README.md
ADDED
package/hal/REAMDE.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# HAL DECORATOR
|
|
2
|
+
|
|
3
|
+
A module to add HAL decoration to JSON responses in Sequelize ORM based projects.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
In your model definition, set decoration on your model
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
const decorator = require("@sera4/essentia).halDecorator;
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
module.exports = (sequelize, DataTypes) => {
|
|
14
|
+
const Post = sequelize.define('Post', {...}, {...});
|
|
15
|
+
// configure the decorator (optional)
|
|
16
|
+
const configs = {
|
|
17
|
+
links: {
|
|
18
|
+
"self" : {
|
|
19
|
+
"get": "/:id/post/"
|
|
20
|
+
},
|
|
21
|
+
"update" : {
|
|
22
|
+
"post": "/:id/post/"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
resource: "/posts",
|
|
26
|
+
exclude : ["created_at", "updated_at"],
|
|
27
|
+
linksTag : "myLinks",
|
|
28
|
+
resourceTag: "myResource"
|
|
29
|
+
}
|
|
30
|
+
decorator.decorate(Post, configs)
|
|
31
|
+
return Post;
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The above configuration (which is optional) should produce the following JSON for each instance of Post sent out to the client:
|
|
36
|
+
```
|
|
37
|
+
{
|
|
38
|
+
"myLinks": {
|
|
39
|
+
"self": {
|
|
40
|
+
"get": "/posts/42801"
|
|
41
|
+
},
|
|
42
|
+
"update": {
|
|
43
|
+
"post": "/posts/42801"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"myResource": {
|
|
47
|
+
"id": 42801,
|
|
48
|
+
"title": "LJitZgbNPLpOlnkYdynKlSYnCjuLBYiVHYMfAPlSALeoVTuNKw",
|
|
49
|
+
"name": "cvnititqxdPnHdomgxJJVGynNInkScmDlkzAALunYFHNRjffqE",
|
|
50
|
+
"content": "LcURropLnJiudElgAVhsBSMRBRNUzHLDiTebZxxpplXdpeGEFa"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
The links use a similar pattern for parameters interpolation like the express route. For example `/posts/:id/by_user/:user_id` becomes `/posts/20/by_user/123`. If no matching attribute is found on the record, the variable is not replaced.
|
package/hal/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
function lastElement(arr) {
|
|
4
|
+
if (arr && arr.length) {
|
|
5
|
+
return arr[arr.length - 1];
|
|
6
|
+
} else {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class HalDecorator {
|
|
12
|
+
|
|
13
|
+
decorate(Model, options) {
|
|
14
|
+
const model = Model.instance || Model;
|
|
15
|
+
const resource = (options && options.resource) ? options.resource : "";
|
|
16
|
+
const exclude = (options && options.exclude) ? options.exclude : [];
|
|
17
|
+
const links = (options && options.links) ? options.links : {};
|
|
18
|
+
const linksTag = (options && options.linksTag) ? options.linksTag : "_links";
|
|
19
|
+
const resourceTag = (options && options.resourceTag) ? options.resourceTag : "_resource";
|
|
20
|
+
|
|
21
|
+
const link = (name, method, l) => {
|
|
22
|
+
if (name && method && l) {
|
|
23
|
+
var aLink = {};
|
|
24
|
+
alink[method] = l;
|
|
25
|
+
links[name] = aLink;
|
|
26
|
+
}
|
|
27
|
+
return model;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const getLinkData = (values) => {
|
|
31
|
+
var linkData = {};
|
|
32
|
+
if (links) {
|
|
33
|
+
for (const key in links) {
|
|
34
|
+
if (links.hasOwnProperty(key)) {
|
|
35
|
+
const link = links[key];
|
|
36
|
+
const method = lastElement(Object.keys(link));
|
|
37
|
+
var url = link[method];
|
|
38
|
+
const paramPattern = /\:([a-zA-z_0-9]*)/;
|
|
39
|
+
const arr = paramPattern.exec(url);
|
|
40
|
+
if (arr) {
|
|
41
|
+
// Replace parameters
|
|
42
|
+
url = `/${resource}/${url.split(arr[0]).join(values[arr[1]])}`;
|
|
43
|
+
// Remove extra slashes that may have been added
|
|
44
|
+
url = url.replace(/\/\/*/g, "/");
|
|
45
|
+
linkData[key] = {}
|
|
46
|
+
linkData[key][method] = url;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return Object.keys(linkData).length > 0 ? linkData : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
model.link = link;
|
|
55
|
+
Model.prototype.toJSON = function() {
|
|
56
|
+
var values = Object.assign({}, this.get());
|
|
57
|
+
if (exclude) {
|
|
58
|
+
exclude.forEach(element => {
|
|
59
|
+
delete values[element];
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const links = getLinkData(values);
|
|
63
|
+
const result = {}
|
|
64
|
+
if (links) {
|
|
65
|
+
result[linksTag] = links;
|
|
66
|
+
}
|
|
67
|
+
result[resourceTag] = values;
|
|
68
|
+
return result;
|
|
69
|
+
};
|
|
70
|
+
console.log(model);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = new HalDecorator()
|
package/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
paginator : require("./paginator/s4-pagination"),
|
|
3
|
+
sqlPaginator : require("./paginator/sql-pagination"),
|
|
3
4
|
logger : require("./logger/s4-logger"),
|
|
4
|
-
lastCommit : require("./last_commit")
|
|
5
|
+
lastCommit : require("./last_commit"),
|
|
6
|
+
halDecorator : require("./hal")
|
|
5
7
|
}
|
package/package.json
CHANGED
package/package.tar.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# PAGINATOR
|
|
2
|
+
|
|
3
|
+
This is a module to add pagination to Sequelize ORM based projects.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
In your model definition, set pagination on your model
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
const paginator = require("@sera4/essentia).sqlPaginator;
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
module.exports = (sequelize, DataTypes) => {
|
|
14
|
+
const Post = sequelize.define('Post', {...}, {...});
|
|
15
|
+
// Add pagination
|
|
16
|
+
paginator.paginate(Post);
|
|
17
|
+
return Post;
|
|
18
|
+
};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Later in your controller you can call the paginate method on your model
|
|
22
|
+
to return paginated results:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
app.get('/posts', (req, res) => {
|
|
26
|
+
Post.using(req.query).paginate()
|
|
27
|
+
.then(p => res.status(200).json(p));
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For example, calling `/posts?page_size=2&page_index=1 will return
|
|
32
|
+
something similar to the following:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
{
|
|
36
|
+
"_pagination": {
|
|
37
|
+
"current_page": 1,
|
|
38
|
+
"first_page": false,
|
|
39
|
+
"last_page": false,
|
|
40
|
+
"page_size": 2,
|
|
41
|
+
"total_pages": 2500,
|
|
42
|
+
"total_size": 5000,
|
|
43
|
+
"out_of_bounds": false,
|
|
44
|
+
"previous_page": 0,
|
|
45
|
+
"next_page": 2
|
|
46
|
+
},
|
|
47
|
+
"_collection": [
|
|
48
|
+
{
|
|
49
|
+
"id": 42803,
|
|
50
|
+
"title": "FYrlujXyrcviLIkQNQsyIXEaRdARCJqNPIDsDiFuLxENtFXPRS",
|
|
51
|
+
"name": "uLHnWSekmNNKueYiglAkAwWckFetoSnudXalelnAkpnayIgiUi",
|
|
52
|
+
"content": "THISkjbnGVprKVFsMnlNEhgDYSGRZcomhNkcrbnuvnqogCdUen",
|
|
53
|
+
"created_at": "2018-11-03T16:04:10.526Z",
|
|
54
|
+
"updated_at": "2018-11-03T16:04:10.526Z"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": 42804,
|
|
58
|
+
"title": "VHgYIBGIuyObzHjrpAwFwbtZSVMnCZdTIAIzzlQHChEPbwGwmU",
|
|
59
|
+
"name": "upvMbeIKAcuwHBruLyPcwEUYlAbQqqljUcMajOyvGaqCTahwfs",
|
|
60
|
+
"content": "EeopiRHsUdNNqzshkafYqkVzACOyWQpdPXCBSzuPBIDHWGqWuk",
|
|
61
|
+
"created_at": "2018-11-03T16:04:10.534Z",
|
|
62
|
+
"updated_at": "2018-11-03T16:04:10.534Z"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `using` method takes 2 parameters:
|
|
69
|
+
|
|
70
|
+
* options - this is where the pagination parameters are read from. By default the method will be looking for `page_size` and `page_index`
|
|
71
|
+
* res - this is the express js response object and the method will also add pagination in the headers (if configured as such)
|
|
72
|
+
|
|
73
|
+
You can configure the pagination globally via `paginator.configure(options)`. The following options are available:
|
|
74
|
+
|
|
75
|
+
* Configuring the url keys for pagination:
|
|
76
|
+
* `paginateIn.size=page_size` - This is the key that will be used to parse number of items to read when the `using` method is called. Renaming this to `per_page` for examle, the url must contain the following query parameters `/posts?per_page=20` to load 20 posts
|
|
77
|
+
* `paginateIn.index=page_index` - This is the key that will be used to parse the offset to read when the `using` method is called. Renaming this to `page` requires an url with `/posts?page_size=20&page=2` to read rows 21 through 40 from the database.
|
|
78
|
+
* Configure the way the pagination data is displayed
|
|
79
|
+
* `paginateOut.current_page=current_page` Label used to show the current page value in the pagination data
|
|
80
|
+
* `paginateOut.first_page=first_page` Label used to show the first page value in the pagination data
|
|
81
|
+
* `paginateOut.last_page=last_page` Label used to show the last page value in the pagination data
|
|
82
|
+
* `paginateOut.next_page=next_page` Label used to show the next page value in the pagination data
|
|
83
|
+
* `paginateOut.out_of_bounds=out_of_bounds` Label used to show the out of bounds value in the pagination data
|
|
84
|
+
* `paginateOut.page_size=page_size` Label used to show the page size value in the pagination data
|
|
85
|
+
* `paginateOut.previous_page=previous_page` Label used to show the previous size value in the pagination data
|
|
86
|
+
* `paginateOut.total_pages=total_pages` Label used to show the total pages value in the pagination data
|
|
87
|
+
* `paginateOut.total_size=total_size` Label used to show the total size value in the pagination data
|
|
88
|
+
* `paginateOut.links=""` Label used to show the links in the pagination data. If this value is empty or null, no links are added in the pagination data. If set to `true`, then a `_links` key is added to the pagination data with links to previous and next pages if available. You can provide a non empty string instead of `true` to use your own lable for this key.
|
|
89
|
+
* Other configurations:
|
|
90
|
+
* `zeroBasedOffset=true` When false, the indexing is done from 1, otherwise it's done from 0 like in arrays
|
|
91
|
+
* `paginationHeader=tws-pagination` The name of the pagination header used when adding pagination in the headers
|
|
92
|
+
* `headersOnly=false` If this is set to true, then the pagination will return an array of objects and all the pagination data can be found in the headers. Otherwise the result will have a key for pagination (`_pagination`) and a key for the results (`_collection`)
|
|
93
|
+
* `paginationTag=_pagination` Used to overwrite the key name used for pagination
|
|
94
|
+
* `collectionTag=_collection` Used to overwrite the key name used for results
|
|
95
|
+
|
|
96
|
+
The configuration is done globally, so you can add something like this as early as possible to your code:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
paginator.configure({
|
|
100
|
+
paginateIn : {
|
|
101
|
+
size: "items_per_page",
|
|
102
|
+
index: "page_number",
|
|
103
|
+
},
|
|
104
|
+
paginateOut : {
|
|
105
|
+
links: "myLinks",
|
|
106
|
+
current_page: "the_current_page"
|
|
107
|
+
},
|
|
108
|
+
zeroBasedOffset : false,
|
|
109
|
+
paginationTag : "paginationData",
|
|
110
|
+
collectionTag : "paginatedCollection"
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This will require an url like `/posts?items_per_page=2&page_number=1` and will produce the following results:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
{
|
|
118
|
+
"paginationData": {
|
|
119
|
+
"the_current_page": 1,
|
|
120
|
+
"first_page": true,
|
|
121
|
+
"last_page": false,
|
|
122
|
+
"page_size": 2,
|
|
123
|
+
"total_pages": 2500,
|
|
124
|
+
"total_size": 5000,
|
|
125
|
+
"out_of_bounds": false,
|
|
126
|
+
"next_page": 2,
|
|
127
|
+
"myLinks": {
|
|
128
|
+
"first_page": "?page_number=1&items_per_page=2",
|
|
129
|
+
"last_page": "?page_number=2500&items_per_page=2",
|
|
130
|
+
"next_page": "?page_number=2&items_per_page=2"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"paginatedCollection": [
|
|
134
|
+
{
|
|
135
|
+
"id": 42801,
|
|
136
|
+
"title": "LJitZgbNPLpOlnkYdynKlSYnCjuLBYiVHYMfAPlSALeoVTuNKw",
|
|
137
|
+
"name": "cvnititqxdPnHdomgxJJVGynNInkScmDlkzAALunYFHNRjffqE",
|
|
138
|
+
"content": "LcURropLnJiudElgAVhsBSMRBRNUzHLDiTebZxxpplXdpeGEFa",
|
|
139
|
+
"created_at": "2018-11-03T16:04:10.511Z",
|
|
140
|
+
"updated_at": "2018-11-03T16:04:10.511Z"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": 42802,
|
|
144
|
+
"title": "mTyXHFoJjgXmwASYvfFgZJcstUTbHjvrcuVoaUMdXZmXuMrfBz",
|
|
145
|
+
"name": "yxrOfMUdiJqNMNdWAfKXbsMsskKbpeRzxfEoicEcSdNDhGaPBE",
|
|
146
|
+
"content": "TEAEugRIeOfceEyREmAGcxldDbtzqlaWGDRMIjAhmfAuwJraot",
|
|
147
|
+
"created_at": "2018-11-03T16:04:10.518Z",
|
|
148
|
+
"updated_at": "2018-11-03T16:04:10.518Z"
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
You may notice that the links in the pagination only have query parameters. If you want to include the resource, provide the resource name when adding pagination to your model.
|
|
155
|
+
|
|
156
|
+
For example:
|
|
157
|
+
```
|
|
158
|
+
const paginator = require("@sera4/essentia).sqlPaginator;
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
module.exports = (sequelize, DataTypes) => {
|
|
162
|
+
const Post = sequelize.define('Post', {...}, {...});
|
|
163
|
+
// Add pagination
|
|
164
|
+
paginator.paginate(Post, "/posts");
|
|
165
|
+
return Post;
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
This will return pagination links in the pagination data prependend with the provided resource:
|
|
169
|
+
```
|
|
170
|
+
"myLinks": {
|
|
171
|
+
"first_page": "?page_number=1&items_per_page=2",
|
|
172
|
+
"last_page": "?page_number=2500&items_per_page=2",
|
|
173
|
+
"next_page": "?page_number=2&items_per_page=2"
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
becomes
|
|
177
|
+
```
|
|
178
|
+
"myLinks": {
|
|
179
|
+
"first_page": "/posts?page_number=1&items_per_page=2",
|
|
180
|
+
"last_page": "/posts?page_number=2500&items_per_page=2",
|
|
181
|
+
"next_page": "/posts?page_number=2&items_per_page=2"
|
|
182
|
+
}
|
|
183
|
+
```
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
class SequelizePaginator {
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
this.paginateIn = {
|
|
7
|
+
size: "page_size",
|
|
8
|
+
index: "page_index"
|
|
9
|
+
};
|
|
10
|
+
this.paginateOut = {
|
|
11
|
+
current_page: "current_page",
|
|
12
|
+
first_page: "first_page",
|
|
13
|
+
last_page: "last_page",
|
|
14
|
+
next_page: "next_page",
|
|
15
|
+
out_of_bounds: "out_of_bounds",
|
|
16
|
+
page_size: "page_size",
|
|
17
|
+
previous_page: "previous_page",
|
|
18
|
+
total_pages: "total_pages",
|
|
19
|
+
total_size: "total_size",
|
|
20
|
+
links: "",
|
|
21
|
+
};
|
|
22
|
+
this.zeroBasedOffset = true;
|
|
23
|
+
this.paginationHeader = "tws-pagination";
|
|
24
|
+
this.headersOnly = false;
|
|
25
|
+
this.paginationTag = "_pagination";
|
|
26
|
+
this.collectionTag = "_collection";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
paginate(Model, resource="") {
|
|
30
|
+
const model = Model.instance || Model;
|
|
31
|
+
var pagination = {limit : 15, offset: 1}
|
|
32
|
+
var response = null;
|
|
33
|
+
var resource = resource;
|
|
34
|
+
|
|
35
|
+
const using = (options, resp) => {
|
|
36
|
+
response = resp;
|
|
37
|
+
var limit = pagination.limit;
|
|
38
|
+
var offset = pagination.offset;
|
|
39
|
+
|
|
40
|
+
if (options && options[this.paginateIn.size]) {
|
|
41
|
+
limit = parseInt(options[this.paginateIn.size]);
|
|
42
|
+
}
|
|
43
|
+
if (options && options[this.paginateIn.index]) {
|
|
44
|
+
offset = parseInt(options[this.paginateIn.index]);
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
47
|
+
limit = 1;
|
|
48
|
+
}
|
|
49
|
+
if (!Number.isInteger(offset) || offset < 0) {
|
|
50
|
+
offset = 0;
|
|
51
|
+
}
|
|
52
|
+
if (!this.zeroBasedOffset) {
|
|
53
|
+
offset = offset <= 1 ? 0 : offset - 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pagination.limit = limit;
|
|
57
|
+
pagination.offset = offset;
|
|
58
|
+
return model;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const paginate = (options) => {
|
|
62
|
+
if (!options) {
|
|
63
|
+
options = {};
|
|
64
|
+
}
|
|
65
|
+
options.limit = pagination.limit;
|
|
66
|
+
options.offset = pagination.limit * pagination.offset;
|
|
67
|
+
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
model.findAndCountAll(options)
|
|
70
|
+
.then(r => {
|
|
71
|
+
var paginationData = getPaginationData(r);
|
|
72
|
+
if (response) {
|
|
73
|
+
response.set(this.paginationHeader, JSON.stringify(paginationData));
|
|
74
|
+
}
|
|
75
|
+
var data = {};
|
|
76
|
+
if (this.headersOnly) {
|
|
77
|
+
data = r.rows
|
|
78
|
+
} else {
|
|
79
|
+
data[this.paginationTag] = addLinks(paginationData);
|
|
80
|
+
data[this.collectionTag] = r.rows;
|
|
81
|
+
}
|
|
82
|
+
resolve(data);
|
|
83
|
+
}).catch(e => reject(e));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const addLinks = (data) => {
|
|
88
|
+
var linksKey = null;
|
|
89
|
+
if (this.paginateOut.links === true) {
|
|
90
|
+
linksKey = "links"
|
|
91
|
+
} else if (this.paginateOut.links) {
|
|
92
|
+
linksKey = this.paginateOut.links;
|
|
93
|
+
}
|
|
94
|
+
if (data && linksKey && !data[this.paginateOut["out_of_bounds"]]) {
|
|
95
|
+
var links = {}
|
|
96
|
+
var firstPageIndex = this.zeroBasedOffset ? 0 : 1;
|
|
97
|
+
var lastPageIndex = this.zeroBasedOffset ? data[this.paginateOut["total_pages"]] - 1 : data[this.paginateOut["total_pages"]];
|
|
98
|
+
links[this.paginateOut["first_page"]] = `${resource}?${this.paginateIn["index"]}=${firstPageIndex}&${this.paginateIn["size"]}=${data[this.paginateOut["page_size"]]}`;
|
|
99
|
+
links[this.paginateOut["last_page"]] = `${resource}?${this.paginateIn["index"]}=${lastPageIndex}&${this.paginateIn["size"]}=${data[this.paginateOut["page_size"]]}`;
|
|
100
|
+
if (!data[this.paginateOut["first_page"]]) {
|
|
101
|
+
links[this.paginateOut["previous_page"]] =
|
|
102
|
+
`${resource}?${this.paginateIn["index"]}=${data[this.paginateOut["previous_page"]]}&${this.paginateIn["size"]}=${data[this.paginateOut["page_size"]]}`;
|
|
103
|
+
}
|
|
104
|
+
if (!data[this.paginateOut["last_page"]]) {
|
|
105
|
+
links[this.paginateOut["next_page"]] =
|
|
106
|
+
`${resource}?${this.paginateIn["index"]}=${data[this.paginateOut["next_page"]]}&${this.paginateIn["size"]}=${data[this.paginateOut["page_size"]]}`;
|
|
107
|
+
}
|
|
108
|
+
data[linksKey] = links;
|
|
109
|
+
}
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const getPaginationData = (result) => {
|
|
114
|
+
var currentPage = this.zeroBasedOffset ? pagination.offset : pagination.offset + 1;
|
|
115
|
+
var outOfBounds = result.rows.length === 0;
|
|
116
|
+
var totalPages = Math.ceil(result.count / pagination.limit);
|
|
117
|
+
var data = {};
|
|
118
|
+
data[this.paginateOut["current_page"]] = currentPage;
|
|
119
|
+
data[this.paginateOut["first_page"]] = outOfBounds ? false : pagination.offset === 0;
|
|
120
|
+
data[this.paginateOut["last_page"]] = outOfBounds ? false : (pagination.offset * pagination.limit) + pagination.limit >= result.count;
|
|
121
|
+
data[this.paginateOut["page_size"]] = pagination.limit;
|
|
122
|
+
data[this.paginateOut["total_pages"]] = totalPages;
|
|
123
|
+
data[this.paginateOut["total_size"]] = result.count;
|
|
124
|
+
data[this.paginateOut["out_of_bounds"]] = outOfBounds;
|
|
125
|
+
if (!outOfBounds) {
|
|
126
|
+
if ((this.zeroBasedOffset && currentPage > 0) || (!this.zeroBasedOffset && currentPage > 1)) {
|
|
127
|
+
data[this.paginateOut["previous_page"]] = currentPage - 1;
|
|
128
|
+
}
|
|
129
|
+
if ((this.zeroBasedOffset && currentPage + 1 < totalPages) || (!this.zeroBasedOffset && currentPage < totalPages)) {
|
|
130
|
+
data[this.paginateOut["next_page"]] = currentPage + 1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return data;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
model.using = using;
|
|
137
|
+
model.paginate = paginate;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
configure(options) {
|
|
141
|
+
// Parse the object options
|
|
142
|
+
if (options.paginateIn) {
|
|
143
|
+
var x = options.paginateIn;
|
|
144
|
+
for (var key in this.paginateIn) {
|
|
145
|
+
if (this.paginateIn.hasOwnProperty(key) && x.hasOwnProperty(key)) {
|
|
146
|
+
this.paginateIn[key] = x[key];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (options.paginateOut) {
|
|
151
|
+
var x = options.paginateOut;
|
|
152
|
+
for (var key in this.paginateOut) {
|
|
153
|
+
if (this.paginateOut.hasOwnProperty(key) && x.hasOwnProperty(key)) {
|
|
154
|
+
this.paginateOut[key] = x[key];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Parse the simple options
|
|
159
|
+
for(var key in this) {
|
|
160
|
+
if (!["paginateIn", "paginateOut"].includes(key)) {
|
|
161
|
+
if (this.hasOwnProperty(key) && options.hasOwnProperty(key)) {
|
|
162
|
+
this[key] = options[key];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = new SequelizePaginator();
|