Skip to content

Commit

Permalink
fix: document service find many pagination (#20178)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marc-Roig committed May 6, 2024
1 parent 01187db commit 8e9b152
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 114 deletions.
@@ -1,6 +1,6 @@
import { omit, pipe } from 'lodash/fp';

import { contentTypes, sanitize, errors } from '@strapi/utils';
import { contentTypes, sanitize, errors, pagination } from '@strapi/utils';
import type { Core, Modules, UID } from '@strapi/types';

import { buildDeepPopulate, getDeepPopulate, getDeepPopulateDraftCount } from './utils/populate';
Expand Down Expand Up @@ -82,23 +82,18 @@ const documentManager = ({ strapi }: { strapi: Core.Strapi }) => {
},

async findPage(opts: DocServiceParams<'findMany'>, uid: UID.CollectionType) {
// Pagination
const page = Number(opts?.page) || 1;
const pageSize = Number(opts?.pageSize) || 10;
const params = pagination.withDefaultPagination(opts || {}, {
maxLimit: 1000,
});

const [documents, total = 0] = await Promise.all([
strapi.documents(uid).findMany(opts),
strapi.documents(uid).count(opts),
strapi.documents(uid).findMany(params),
strapi.documents(uid).count(params),
]);

return {
results: documents,
pagination: {
page,
pageSize,
pageCount: Math.ceil(total! / pageSize),
total,
},
pagination: pagination.transformPagedPaginationInfo(params, total),
};
},

Expand Down
Expand Up @@ -27,8 +27,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: defaultLimit,
start: 0,
limit: defaultLimit,
});
});

Expand All @@ -38,8 +38,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: pagination.pageSize,
start: 0,
limit: pagination.pageSize,
});
});

Expand All @@ -48,8 +48,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: maxLimit,
start: 0,
limit: maxLimit,
});
});

Expand All @@ -58,8 +58,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: 1,
start: 0,
limit: 1,
});
});

Expand All @@ -68,8 +68,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: 1,
start: 0,
limit: 1,
});
});

Expand All @@ -78,8 +78,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: 1,
start: 0,
limit: 1,
});
});
});
Expand Down Expand Up @@ -161,8 +161,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: defaultLimit,
start: 0,
limit: defaultLimit,
});
});

Expand All @@ -172,8 +172,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: pagination.pageSize,
start: 0,
limit: pagination.pageSize,
});
});

Expand All @@ -182,8 +182,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: pagination.pageSize,
start: 0,
limit: pagination.pageSize,
});
});

Expand All @@ -192,8 +192,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: 1,
start: 0,
limit: 1,
});
});

Expand All @@ -202,8 +202,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: 1,
start: 0,
limit: 1,
});
});

Expand All @@ -212,8 +212,8 @@ describe('Pagination service', () => {
const paginationInfo = getPaginationInfo({ pagination });

expect(paginationInfo).toEqual({
page: 1,
pageSize: 1,
start: 0,
limit: 1,
});
});
});
Expand Down
12 changes: 9 additions & 3 deletions packages/core/core/src/core-api/service/collection-type.ts
@@ -1,6 +1,11 @@
import type { Core, Struct, Modules } from '@strapi/types';

import { getPaginationInfo, shouldCount, transformPaginationResponse } from './pagination';
import {
getPaginationInfo,
shouldCount,
isPagedPagination,
transformPaginationResponse,
} from './pagination';

import { CoreService } from './core-service';

Expand All @@ -22,6 +27,7 @@ export class CollectionTypeService
const fetchParams = this.getFetchParams(params);

const paginationInfo = getPaginationInfo(fetchParams);
const isPaged = isPagedPagination(fetchParams.pagination);

const results = await strapi.documents(uid).findMany({
...fetchParams,
Expand All @@ -37,13 +43,13 @@ export class CollectionTypeService

return {
results,
pagination: transformPaginationResponse(paginationInfo, count),
pagination: transformPaginationResponse(paginationInfo, count, isPaged),
};
}

return {
results,
pagination: paginationInfo,
pagination: transformPaginationResponse(paginationInfo, undefined, isPaged),
};
}

Expand Down
93 changes: 28 additions & 65 deletions packages/core/core/src/core-api/service/pagination.ts
@@ -1,5 +1,6 @@
import { has, toNumber, isUndefined } from 'lodash/fp';
import { errors } from '@strapi/utils';
import { omit, has, toNumber } from 'lodash/fp';

import { errors, pagination } from '@strapi/utils';

interface BasePaginationParams {
withCount?: boolean | 't' | '1' | 'true' | 'f' | '0' | 'false' | 0 | 1;
Expand Down Expand Up @@ -35,14 +36,11 @@ const getLimitConfigDefaults = () => ({
maxLimit: toNumber(strapi.config.get('api.rest.maxLimit')) || null,
});

/**
* Should maxLimit be used as the limit or not
*/
const shouldApplyMaxLimit = (
limit: number,
maxLimit: number | null,
{ isPagedPagination = false } = {}
) => (!isPagedPagination && limit === -1) || (maxLimit !== null && limit > maxLimit);
const isOffsetPagination = (pagination?: PaginationParams): pagination is OffsetPagination =>
has('start', pagination) || has('limit', pagination);

const isPagedPagination = (pagination?: PaginationParams): pagination is PagedPagination =>
has('page', pagination) || has('pageSize', pagination) || !isOffsetPagination(pagination);

const shouldCount = (params: { pagination?: PaginationParams }) => {
if (has('pagination.withCount', params)) {
Expand Down Expand Up @@ -72,69 +70,34 @@ const shouldCount = (params: { pagination?: PaginationParams }) => {
return Boolean(strapi.config.get('api.rest.withCount', true));
};

const isOffsetPagination = (pagination?: PaginationParams): pagination is OffsetPagination =>
has('start', pagination) || has('limit', pagination);

const isPagedPagination = (pagination?: PaginationParams): pagination is PagedPagination =>
has('page', pagination) || has('pageSize', pagination);

const getPaginationInfo = (params: { pagination?: PaginationParams }): PaginationInfo => {
const { defaultLimit, maxLimit } = getLimitConfigDefaults();

const { pagination } = params;

const isPaged = isPagedPagination(pagination);
const isOffset = isOffsetPagination(pagination);
const { start, limit } = pagination.withDefaultPagination(params.pagination || {}, {
defaults: { offset: { limit: defaultLimit }, page: { pageSize: defaultLimit } },
maxLimit: maxLimit || -1,
});

if (isOffset && isPaged) {
throw new errors.ValidationError(
'Invalid pagination parameters. Expected either start/limit or page/pageSize'
);
}

if (!isOffset && !isPaged) {
return {
page: 1,
pageSize: defaultLimit,
};
}

if (isPagedPagination(pagination)) {
const pageSize = isUndefined(pagination.pageSize)
? defaultLimit
: Math.max(1, toNumber(pagination.pageSize));

return {
page: Math.max(1, toNumber(pagination.page || 1)),
pageSize:
typeof maxLimit === 'number' &&
shouldApplyMaxLimit(pageSize, maxLimit, { isPagedPagination: true })
? maxLimit
: Math.max(1, pageSize),
};
}
return { start, limit };
};

const limit = isUndefined(pagination.limit) ? defaultLimit : toNumber(pagination.limit);
const transformPaginationResponse = (
paginationInfo: PaginationInfo,
total: number | undefined,
isPaged: boolean
) => {
const transform = isPaged
? pagination.transformPagedPaginationInfo
: pagination.transformOffsetPaginationInfo;

return {
start: Math.max(0, toNumber(pagination.start || 0)),
limit: shouldApplyMaxLimit(limit, maxLimit) ? maxLimit || -1 : Math.max(1, limit),
};
};
const paginationResponse = transform(paginationInfo, total!);

const transformPaginationResponse = (paginationInfo: PaginationInfo, count: number) => {
if ('page' in paginationInfo) {
return {
...paginationInfo,
pageCount: Math.ceil(count / paginationInfo.pageSize),
total: count,
};
if (!total) {
// Ignore total and pageCount if `total` value is not available.
return omit(['total', 'pageCount'], paginationResponse) as ReturnType<typeof transform>;
}

return {
...paginationInfo,
total: count,
};
return paginationResponse;
};

export { getPaginationInfo, transformPaginationResponse, shouldCount };
export { isPagedPagination, shouldCount, getPaginationInfo, transformPaginationResponse };

0 comments on commit 8e9b152

Please sign in to comment.