1 | const qs = require('qs')
|
2 | const _ = require('lodash')
|
3 |
|
4 | /**
|
5 | * @function
|
6 | * @private
|
7 | *
|
8 | * @description
|
9 | * Get parameter value based on the passed condition
|
10 | *
|
11 | * @param {*} param The parameter to be minimized
|
12 | * @param {*} condition The condition to be checked for
|
13 | * @returns {* | undefined} The minimized parameter value
|
14 | */
|
15 | function minimizeQueryParameter (param, condition) {
|
16 | return param === condition ? undefined : param
|
17 | }
|
18 |
|
19 | /**
|
20 | * @function
|
21 | * @private
|
22 | *
|
23 | * @description
|
24 | * Halify links by converting them to objects
|
25 | *
|
26 | * @param {Object} links The links to be halified
|
27 | * @returns {Object} The halified links object
|
28 | */
|
29 | function halifyLinks (links) {
|
30 | _.forOwn(links, (href, entity) => {
|
31 | links[entity] = { href }
|
32 | })
|
33 | }
|
34 |
|
35 | /**
|
36 | * @function
|
37 | * @private
|
38 | *
|
39 | * @description
|
40 | * Get absolute/relative request url
|
41 | *
|
42 | * @param {Object} request The related request object
|
43 | * @param {Object} pluginOptions The plugin related options
|
44 | * @returns {string} The request url
|
45 | */
|
46 | function getRequestUrl (request, pluginOptions) {
|
47 | const proxyProtocol = request.headers && request.headers['x-forwarded-proto']
|
48 | const protocol = proxyProtocol || request.server.info.protocol || 'http'
|
49 | let requestUrl = request.url.pathname
|
50 |
|
51 | if (pluginOptions.absolute) {
|
52 | requestUrl = `${protocol}://${request.info.host}${requestUrl}`
|
53 | }
|
54 |
|
55 | return requestUrl
|
56 | }
|
57 |
|
58 | /**
|
59 | * @function
|
60 | * @private
|
61 | *
|
62 | * @description
|
63 | * Get link of the requested resource itself
|
64 | *
|
65 | * @param {number} page The requested page
|
66 | * @param {number} perPage The requested items per page
|
67 | * @param {Object} request The related request object
|
68 | * @param {Object} options The request related options
|
69 | * @param {Object} pluginOptions The plugin related options
|
70 | * @returns {string} The generated resource link
|
71 | */
|
72 | function getSelfLink (page, perPage, request, options, pluginOptions) {
|
73 | const requestPath = getRequestUrl(request, pluginOptions)
|
74 |
|
75 | const paramNames = pluginOptions.paramNames
|
76 | const query = qs.stringify(Object.assign({}, request.query, {
|
77 | [paramNames.perPage]: minimizeQueryParameter(perPage, options.perPage),
|
78 | [paramNames.page]: minimizeQueryParameter(page, 1)
|
79 | }))
|
80 |
|
81 | return query ? `${requestPath}?${query}` : requestPath
|
82 | }
|
83 |
|
84 | /**
|
85 | * @function
|
86 | * @private
|
87 | *
|
88 | * @description
|
89 | * Get pagination link generator with predefined values
|
90 | *
|
91 | * @param {string} id The endpoint ID
|
92 | * @param {number} perPage The number of entries per page
|
93 | * @param {Object} options The related options
|
94 | * @param {Object} requestObj The related query parameters
|
95 | * @param {Function} aka The request bound akaya function for link building
|
96 | * @param {boolean} absolute If the link should be an absolute one
|
97 | * @param {Object} paramNames The names of the query params
|
98 | * @returns {Function} The predefined pagination link generator
|
99 | */
|
100 | function getPaginationLink (id, perPage, options, requestObj, aka, absolute, paramNames) {
|
101 | perPage = minimizeQueryParameter(perPage, options.perPage)
|
102 |
|
103 | return (page) => {
|
104 | page = minimizeQueryParameter(page, 1)
|
105 |
|
106 | return aka(id, {
|
107 | query: Object.assign({}, requestObj.query, {
|
108 | [paramNames.page]: page,
|
109 | [paramNames.perPage]: perPage
|
110 | }),
|
111 | params: requestObj.params
|
112 | }, { rel: !absolute })
|
113 | }
|
114 | }
|
115 |
|
116 | /**
|
117 | * @function
|
118 | * @public
|
119 | *
|
120 | * @description
|
121 | * Get entity/href mapping of necessary pagination links
|
122 | *
|
123 | * @param {string} id The endpoint ID
|
124 | * @param {number} page The requested page
|
125 | * @param {number} perPage The number of entries per page
|
126 | * @param {number} total The total number of entries
|
127 | * @param {Object} requestObj The related query parameters
|
128 | * @param {Function} aka The request bound akaya function for link building
|
129 | * @param {Object} options The related options
|
130 | * @param {Object} pluginOptions The plugin related options
|
131 | * @returns {Object.<?string>} The mapping of pagination links
|
132 | */
|
133 | function getPaginationLinks (id, page, perPage, total, requestObj, aka, options, pluginOptions) {
|
134 | const getLink = getPaginationLink(
|
135 | id, perPage, options, requestObj, aka, pluginOptions.absolute, pluginOptions.paramNames
|
136 | )
|
137 | const lastPage = Math.ceil(total / perPage)
|
138 | const links = {}
|
139 |
|
140 | links.self = getSelfLink(page, perPage, requestObj, options, pluginOptions)
|
141 | links.first = getLink(undefined)
|
142 |
|
143 | if (page > 1 && page <= lastPage) {
|
144 | links.prev = getLink(page - 1)
|
145 | }
|
146 |
|
147 | if (page < lastPage && page >= 1) {
|
148 | links.next = getLink(page + 1)
|
149 | }
|
150 |
|
151 | links.last = getLink(lastPage)
|
152 |
|
153 | halifyLinks(links)
|
154 |
|
155 | return links
|
156 | }
|
157 |
|
158 | module.exports = {
|
159 | getLinks: getPaginationLinks
|
160 | }
|