From 88fa25974eb543459e0038786bd6ab3e3891267a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maksymilian=20Pamu=C5=82a?= Date: Sun, 3 Aug 2025 17:59:28 +0200 Subject: [PATCH] fix/316: fetch pagination with their children --- server/src/controllers/client.controller.ts | 4 +- server/src/services/common.service.ts | 43 ++++++++++++------- server/src/services/utils/functions.ts | 23 +++++----- .../client.controller.validator.ts | 2 + server/src/validators/utils.ts | 10 ++--- 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/server/src/controllers/client.controller.ts b/server/src/controllers/client.controller.ts index 95f998c..6d7ff43 100644 --- a/server/src/controllers/client.controller.ts +++ b/server/src/controllers/client.controller.ts @@ -50,7 +50,9 @@ const controllers = ({ strapi }: StrapiContext) => ({ const result = clientValidator.findAllInHierarchyValidator(config.enabledCollections, ctx.params.relation, ctx.query); if (isRight(result)) { return this.getService('common').findAllInHierarchy( - flatInput(result.right), + flatInput( + result.right + ) ); } throw throwError(ctx, unwrapEither(result)); diff --git a/server/src/services/common.service.ts b/server/src/services/common.service.ts index 58a0c6b..85f1651 100644 --- a/server/src/services/common.service.ts +++ b/server/src/services/common.service.ts @@ -1,15 +1,6 @@ import { Params } from '@strapi/database/dist/entity-manager/types'; import { UID } from '@strapi/strapi'; -import { - first, - get, - isNil, - isObject, - isString, - omit as filterItem, - parseInt, - uniq, -} from 'lodash'; +import { first, get, isNil, isObject, isString, omit as filterItem, parseInt, uniq } from 'lodash'; import { isProfane, replaceProfanities } from 'no-profanity'; import { Id, PathTo, PathValue, RelatedEntity, StrapiContext } from '../@types'; import { CommentsPluginConfig } from '../config'; @@ -56,7 +47,7 @@ const commonService = ({ strapi }: StrapiContext) => ({ return user ? user.id != undefined : true; }, - sanitizeCommentEntity(entity: Comment | CommentWithRelated, blockedAuthors: string[], omitProps: Array = [], populate: any = {}): Comment { + sanitizeCommentEntity(entity: (Comment | CommentWithRelated) & { children?: Comment | CommentWithRelated[] }, blockedAuthors: string[], omitProps: Array = [], populate: any = {}): Comment { const fieldsToPopulate = Array.isArray(populate) ? populate : Object.keys(populate || {}); return filterItem({ ...buildAuthorModel( @@ -119,10 +110,12 @@ const commonService = ({ strapi }: StrapiContext) => ({ where: { threadOf: _.id, }, + pageSize: Infinity, }); return { id: _.id, itemsInTread: total, + children: results, firstThreadItemId: first(results)?.id, }; }), @@ -144,6 +137,9 @@ const commonService = ({ strapi }: StrapiContext) => ({ return this.sanitizeCommentEntity( { ..._, + // @ts-ignore + children: (threadedItem?.children || []) + .map((item) => this.sanitizeCommentEntity(item, doNotPopulateAuthor, omit as Array, authorUserPopulate)), threadOf: primitiveThreadOf || _.threadOf, gotThread: (threadedItem?.itemsInTread || 0) > 0, threadFirstItemId: threadedItem?.firstThreadItemId, @@ -173,10 +169,25 @@ const commonService = ({ strapi }: StrapiContext) => ({ omit = [], locale, limit, + pagination, }: clientValidator.FindAllInHierarchyValidatorSchema, relatedEntity?: any, ) { - const entities = await this.findAllFlat({ filters, populate, sort, fields, isAdmin, omit, locale, limit }, relatedEntity); + const entities = await this.findAllFlat({ + filters: { + threadOf: { $null: true }, // Only root comments and the next query will be fetch children + ...filters, + }, + pagination, + populate, + sort, + fields, + isAdmin, + omit, + locale, + limit, + }, relatedEntity); + // console.log('entities', entities); return buildNestedStructure( entities?.data, startingFromId, @@ -359,11 +370,11 @@ const commonService = ({ strapi }: StrapiContext) => ({ .updateMany({ where: { related, - $or: [{ locale }, defaultLocale === locale ? { locale: { $eq: null } } : null].filter(Boolean) + $or: [{ locale }, defaultLocale === locale ? { locale: { $eq: null } } : null].filter(Boolean), }, data: { removed: true, - } + }, }); }, @@ -373,11 +384,11 @@ const commonService = ({ strapi }: StrapiContext) => ({ .updateMany({ where: { related, - $or: [{ locale }, defaultLocale === locale ? { locale: { $eq: null } } : null].filter(Boolean) + $or: [{ locale }, defaultLocale === locale ? { locale: { $eq: null } } : null].filter(Boolean), }, data: { removed: false, - } + }, }); }, diff --git a/server/src/services/utils/functions.ts b/server/src/services/utils/functions.ts index daba54e..1703bd4 100644 --- a/server/src/services/utils/functions.ts +++ b/server/src/services/utils/functions.ts @@ -10,11 +10,12 @@ interface StrapiAuthorUser { username: string; email: string; avatar?: string | object; + [key: string]: unknown; } export const buildNestedStructure = ( - entities: Array, + entities: Array }>, id: Id | null = null, field: string = 'threadOf', dropBlockedThreads = false, @@ -35,21 +36,19 @@ export const buildNestedStructure = ( (isObject(entityField) && (entityField as any).id === id) ); }) - .map((entity: Comment) => ({ + .map((entity: Comment & { children?: Array }) => ({ ...entity, [field]: undefined, related: undefined, blockedThread: blockNestedThreads || entity.blockedThread, - children: - entity.blockedThread && dropBlockedThreads - ? [] - : buildNestedStructure( - entities, - entity.id, - field, - dropBlockedThreads, - entity.blockedThread, - ), + children: entity.blockedThread && dropBlockedThreads ? [] : entity.children ? entity.children : + buildNestedStructure( + entities, + entity.id, + field, + dropBlockedThreads, + entity.blockedThread, + ), })); export const getRelatedGroups = (related: string): Array => diff --git a/server/src/validators/api/controllers/client.controller.validator.ts b/server/src/validators/api/controllers/client.controller.validator.ts index 6206b07..a537707 100644 --- a/server/src/validators/api/controllers/client.controller.validator.ts +++ b/server/src/validators/api/controllers/client.controller.validator.ts @@ -69,6 +69,7 @@ const getBaseFindSchema = (enabledCollections: string[]) => { blockedThread: true, approvalStatus: true, isAdminComment: true, + threadOf: true, }); return z .object({ @@ -117,6 +118,7 @@ export const findAllInHierarchyValidator = (enabledCollections: string[], relati skip: true, relation: true, locale: true, + pagination: true, }) .merge(z.object({ startingFromId: z.number().optional(), diff --git a/server/src/validators/utils.ts b/server/src/validators/utils.ts index fc7d799..7cf13db 100644 --- a/server/src/validators/utils.ts +++ b/server/src/validators/utils.ts @@ -68,7 +68,7 @@ export const filtersValidator = z.union([ endWithValidators, containsValidators, notContainsValidators, - z.object({ $null: z.string().min(1) }), + z.object({ $null: z.boolean() }), z.object({ $notNull: z.boolean() }), ]); @@ -115,11 +115,11 @@ export const queryPaginationSchema = z.object({ pageSize: stringToNumberValidato getFiltersOperators({ blocked: true, blockedThread: true, - }) - ).optional() - }) + }), + ).optional(), + }), ).optional(), - })) + })); export const validate = (result: z.SafeParseReturnType) => { if (!result.success) {