Implement REST API pagination with paged or offset and withCount

This commit is contained in:
Alexandre Bodin 2021-08-31 12:45:25 +02:00
parent 7eaa8ae95e
commit b44a552eee
9 changed files with 150 additions and 58 deletions

View File

@ -6,6 +6,7 @@ testApp/**
examples/**
cypress/**
packages/generators/generators/lib/files/
packages/generators/app/lib/resources/files/
packages/core/helper-plugin/build/**
packages/core/helper-plugin/lib/src/components/**
packages/core/helper-plugin/lib/src/testUtils/**

View File

@ -0,0 +1,7 @@
module.exports = {
rest: {
defaultLimit: 25,
maxLimit: 30,
withCount: false,
},
};

View File

@ -1,6 +1,6 @@
'use strict';
const { sanitizeEntity } = require('@strapi/utils');
const { sanitizeEntity, contentTypes } = require('@strapi/utils');
const { transformResponse } = require('./transform');
const createSingleTypeController = require('./single-type');
@ -18,7 +18,7 @@ module.exports = ({ service, model }) => {
},
};
if (model.kind === 'singleType') {
if (contentTypes.isSingleType(model)) {
return createSingleTypeController(ctx);
}

View File

@ -7,6 +7,13 @@ const {
constants: { PUBLISHED_AT_ATTRIBUTE },
} = require('@strapi/utils').contentTypes;
const {
applyDefaultPagination,
convertPagedToStartLimit,
shouldCount,
formatPaginationResponse,
} = require('./pagination');
const setPublishedAt = data => {
data[PUBLISHED_AT_ATTRIBUTE] = propOr(new Date(), PUBLISHED_AT_ATTRIBUTE, data);
};
@ -21,10 +28,30 @@ const createCollectionTypeService = ({ model, strapi, utils }) => {
const { sanitizeInput, getFetchParams } = utils;
return {
find(opts = {}) {
async find(opts = {}) {
const params = getFetchParams(opts.params);
return strapi.entityService.findPage(uid, { params });
const paginationInfo = applyDefaultPagination(params);
const results = await strapi.entityService.find(uid, {
params: { ...params, ...convertPagedToStartLimit(paginationInfo) },
});
if (shouldCount(params)) {
const count = await strapi.entityService.count(uid, {
params: { ...params, ...paginationInfo },
});
return {
results,
pagination: formatPaginationResponse(paginationInfo, count),
};
}
return {
results,
pagination: paginationInfo,
};
},
findOne(entityId, opts = {}) {

View File

@ -26,54 +26,6 @@ const createService = ({ model, strapi }) => {
return createCollectionTypeService({ model, strapi, utils });
};
/**
* Default limit values from config
* @return {{maxLimit: number, defaultLimit: number}}
*/
const getLimitConfigDefaults = () => ({
defaultLimit: _.toNumber(strapi.config.get('api.rest.defaultLimit', 100)),
maxLimit: _.toNumber(strapi.config.get('api.rest.maxLimit')) || null,
});
/**
* if there is max limit set and limit exceeds this number, return configured max limit
* @param {number} limit - limit you want to cap
* @param {number?} maxLimit - maxlimit used has capping
* @returns {number}
*/
const applyMaxLimit = (limit, maxLimit) => {
if (maxLimit && (limit === -1 || limit > maxLimit)) {
return maxLimit;
}
return limit;
};
const applyDefaultPagination = params => {
const { defaultLimit, maxLimit } = getLimitConfigDefaults();
if (_.isUndefined(params.pagination) || !_.isPlainObject(params.pagination)) {
return {
limit: defaultLimit,
};
}
const { pagination } = params;
if (!_.isUndefined(pagination.pageSize)) {
return {
page: pagination.page,
pageSize: applyMaxLimit(_.toNumber(pagination.pageSize), maxLimit),
};
}
const limit = _.isUndefined(pagination.limit) ? defaultLimit : _.toNumber(pagination.limit);
return {
start: pagination.start,
limit: applyMaxLimit(limit, maxLimit),
};
};
/**
* Create default fetch params
* @param {*} params
@ -83,7 +35,6 @@ const getFetchParams = (params = {}) => {
return {
publicationState: DP_PUB_STATE_LIVE,
...params,
pagination: applyDefaultPagination(params),
};
};

View File

@ -0,0 +1,101 @@
'use strict';
const _ = require('lodash');
/**
* Default limit values from config
* @return {{maxLimit: number, defaultLimit: number}}
*/
const getLimitConfigDefaults = () => ({
defaultLimit: _.toNumber(strapi.config.get('api.rest.defaultLimit', 25)),
maxLimit: _.toNumber(strapi.config.get('api.rest.maxLimit')) || null,
});
/**
* if there is max limit set and limit exceeds this number, return configured max limit
* @param {number} limit - limit you want to cap
* @param {number?} maxLimit - maxlimit used has capping
* @returns {number}
*/
const applyMaxLimit = (limit, maxLimit) => {
if (maxLimit && (limit === -1 || limit > maxLimit)) {
return maxLimit;
}
return limit;
};
const shouldCount = params => {
if (_.has(params, 'pagination.withCount')) {
return params.pagination.withCount === 'false' ? false : true;
}
return Boolean(strapi.config.get('api.rest.withCount', true));
};
const applyDefaultPagination = params => {
const { defaultLimit, maxLimit } = getLimitConfigDefaults();
if (_.isUndefined(params.pagination) || !_.isPlainObject(params.pagination)) {
return {
start: 0,
limit: defaultLimit,
};
}
const { pagination } = params;
if (!_.isUndefined(pagination.pageSize) || !_.isUndefined(pagination.page)) {
const pageSize = _.isUndefined(pagination.pageSize)
? defaultLimit
: Math.max(1, _.toNumber(pagination.pageSize));
return {
page: Math.max(1, _.toNumber(pagination.page || 1)),
pageSize: applyMaxLimit(pageSize, maxLimit),
};
}
const limit = _.isUndefined(pagination.limit)
? defaultLimit
: Math.max(1, _.toNumber(pagination.limit));
return {
start: Math.max(0, _.toNumber(pagination.start || 0)),
limit: applyMaxLimit(limit, maxLimit),
};
};
const convertPagedToStartLimit = pagination => {
if (_.has(pagination, 'page')) {
const { page, pageSize } = pagination;
return {
start: (page - 1) * pageSize,
limit: pageSize,
};
}
return pagination;
};
const formatPaginationResponse = (paginationInfo, count) => {
if (paginationInfo.page) {
return {
...paginationInfo,
pageCount: Math.ceil(count / paginationInfo.pageSize),
total: count,
};
}
return {
...paginationInfo,
total: count,
};
};
module.exports = {
applyDefaultPagination,
convertPagedToStartLimit,
shouldCount,
formatPaginationResponse,
};

View File

@ -14,7 +14,8 @@ const createSingleTypeService = ({ model, strapi, utils }) => {
* @return {Promise}
*/
find({ params } = {}) {
return strapi.entityService.find(uid, { params: getFetchParams(params) });
const normalizedParams = getFetchParams(params);
return strapi.entityService.find(uid, { params: normalizedParams });
},
/**

View File

@ -2,7 +2,7 @@
const { cloneDeep } = require('lodash/fp');
const _ = require('lodash');
const { hasDraftAndPublish } = require('@strapi/utils').contentTypes;
const { hasDraftAndPublish, getPrivateAttributes } = require('@strapi/utils').contentTypes;
const {
CREATED_AT_ATTRIBUTE,
UPDATED_AT_ATTRIBUTE,
@ -60,9 +60,7 @@ const createContentType = (uid, definition) => {
Object.defineProperty(schema, 'privateAttributes', {
get() {
// FIXME: to fix
// return strapi.getModel(model.uid).privateAttributes;
return [];
return getPrivateAttributes(schema);
},
});

View File

@ -0,0 +1,6 @@
module.exports = {
rest: {
defaultLimit: 25,
maxLimit: 100,
},
};