From 5dfd8d83175d9abf67d3f93558afb08d3c158e52 Mon Sep 17 00:00:00 2001 From: Sergei Samokhvalov Date: Wed, 16 Oct 2024 14:08:05 +0300 Subject: [PATCH] Add support pagination to getRelations (#193) * Add support pagination to getRelations * Add Report to scope * Add optional support pagination * Add flag isPagination * Add order by createdAt and addtional where by scope * Add order by createdAt and addtional where by scope * Fix where by scope * Add to response-presenter - relations field * Refactor Co-authored-by: Evgenij Shangin * Refactor Co-authored-by: Evgenij Shangin * Refactor --------- Co-authored-by: Evgenij Shangin --- api/constants.ts | 1 + src/components/response-presenter.ts | 4 ++ src/const/common.ts | 9 ++++ src/controllers/entries.ts | 12 ++++- .../entry/actions/get-entry-relations.ts | 51 +++++++++++++++++-- .../entry/actions/get-related-entries.ts | 37 +++++++++++--- src/utils/utils.ts | 12 ----- 7 files changed, 102 insertions(+), 24 deletions(-) diff --git a/api/constants.ts b/api/constants.ts index 205d02d9..7a71397c 100644 --- a/api/constants.ts +++ b/api/constants.ts @@ -18,6 +18,7 @@ export { RETURN_FAVORITES_COLUMNS, RETURN_NAVIGATION_COLUMNS, INTER_TENANT_GET_ENTRIES_SCHEMA, + ALLOWED_ENTRIES_SCOPE, AUTHORIZATION_HEADER, DL_AUTH_HEADER_KEY, } from '../src/const'; diff --git a/src/components/response-presenter.ts b/src/components/response-presenter.ts index 9c414550..8a381a06 100644 --- a/src/components/response-presenter.ts +++ b/src/components/response-presenter.ts @@ -58,6 +58,10 @@ export async function prepareResponseAsync({data}: {data: any}): Promise { @@ -243,10 +244,19 @@ export default { entryId: params.entryId, direction: query.direction as Optional, includePermissionsInfo: Utils.isTrueArg(query.includePermissionsInfo), + page: (query.page && Number(query.page)) as number | undefined, + pageSize: (query.pageSize && Number(query.pageSize)) as number | undefined, + scope: query.scope as EntryScope | undefined, }, ); - const {code, response} = await prepareResponseAsync({data: result}); + // TODO: leave a response with pagination only, when there will be pagination support everywhere in the frontend + const formattedResponse = + typeof query.page !== 'undefined' && typeof query.pageSize !== 'undefined' + ? result + : result.relations; + + const {code, response} = await prepareResponseAsync({data: formattedResponse}); res.status(code).send(response); }, diff --git a/src/services/entry/actions/get-entry-relations.ts b/src/services/entry/actions/get-entry-relations.ts index 01f30031..491c0f21 100644 --- a/src/services/entry/actions/get-entry-relations.ts +++ b/src/services/entry/actions/get-entry-relations.ts @@ -3,18 +3,22 @@ import {Entry, EntryColumn} from '../../../db/models/new/entry'; import {DlsActions} from '../../../types/models'; import {ServiceArgs} from '../../new/types'; import Utils from '../../../utils'; -import {US_ERRORS} from '../../../const'; +import {ALLOWED_ENTRIES_SCOPE, US_ERRORS} from '../../../const'; import {makeSchemaValidator} from '../../../components/validation-schema-compiler'; import {getWorkbook} from '../../new/workbook/get-workbook'; import {getEntryPermissionsByWorkbook} from '../../new/workbook/utils'; import {getRelatedEntries, RelationDirection} from './get-related-entries'; import {registry} from '../../../registry'; import {getReplica} from '../../new/utils'; +import {EntryScope} from '../../../db/models/new/entry/types'; export type GetEntryRelationsArgs = { entryId: string; direction?: RelationDirection; includePermissionsInfo?: boolean; + scope?: EntryScope; + page?: number; + pageSize?: number; }; const validateArgs = makeSchemaValidator({ @@ -31,6 +35,19 @@ const validateArgs = makeSchemaValidator({ includePermissionsInfo: { type: 'boolean', }, + scope: { + type: 'string', + enum: ALLOWED_ENTRIES_SCOPE, + }, + page: { + type: 'number', + minimum: 0, + }, + pageSize: { + type: 'number', + minimum: 1, + maximum: 200, + }, }, }); @@ -42,11 +59,21 @@ export async function getEntryRelations( const {tenantId, isPrivateRoute} = ctx.get('info'); - const {entryId, direction = RelationDirection.Parent, includePermissionsInfo = false} = args; + const { + entryId, + scope, + page, + pageSize, + direction = RelationDirection.Parent, + includePermissionsInfo = false, + } = args; ctx.log('GET_ENTRY_RELATIONS_REQUEST', { entryId: Utils.encodeId(entryId), direction, + scope, + page, + pageSize, includePermissionsInfo, }); @@ -74,9 +101,24 @@ export async function getEntryRelations( { entryIds: [entryId], direction, + scope, + page, + pageSize, }, ); + const isPagination = typeof page !== 'undefined' && typeof pageSize !== 'undefined'; + + let nextPageToken; + + if (isPagination) { + nextPageToken = Utils.getOptimisticNextPageToken({ + page: page, + pageSize: pageSize, + curPage: relations, + }); + } + relations = relations.filter((item) => item.tenantId === entry.tenantId); if (entry.workbookId) { @@ -138,5 +180,8 @@ export async function getEntryRelations( ctx.log('GET_ENTRY_RELATIONS_SUCCESS', {count: relations.length}); - return relations; + return { + relations, + nextPageToken, + }; } diff --git a/src/services/entry/actions/get-related-entries.ts b/src/services/entry/actions/get-related-entries.ts index b34feebe..1e421c1c 100644 --- a/src/services/entry/actions/get-related-entries.ts +++ b/src/services/entry/actions/get-related-entries.ts @@ -8,6 +8,7 @@ import { } from '../../../const'; import {getReplica} from '../../new/utils'; import {EntryScope} from '../../../db/models/new/entry/types'; +import {EntryColumn} from '../../../db/models/new/entry'; export enum RelationDirection { Parent = 'parent', @@ -18,6 +19,9 @@ type GetRelatedEntriesData = { entryIds: string[]; direction?: RelationDirection; extendedTimeout?: boolean; + scope?: EntryScope; + page?: number; + pageSize?: number; }; type GetRelatedEntriesResult = { @@ -38,14 +42,17 @@ export async function getRelatedEntries( { entryIds, direction = RelationDirection.Parent, + scope, + page, + pageSize, extendedTimeout = false, }: GetRelatedEntriesData, ) { - ctx.log('GET_RELATED_ENTRIES_RUN'); + ctx.log('GET_RELATED_ENTRIES_RUN', {scope, page, pageSize}); const endToStart = direction === RelationDirection.Parent; - const relatedEntries = (await Entry.query(getReplica(trx)) + const relatedEntries = Entry.query(getReplica(trx)) .withRecursive('relatedEntries', (qb) => { qb.select(['fromId', 'toId', raw('1 depth')]) .from('links') @@ -82,14 +89,28 @@ export async function getRelatedEntries( .join('revisions', 'entries.savedId', 'revisions.revId') .as('re'); }) - .orderBy('depth') - .timeout( - extendedTimeout ? EXTENDED_QUERY_TIMEOUT : DEFAULT_QUERY_TIMEOUT, - )) as unknown[] as GetRelatedEntriesResult[]; + .where((builder) => { + if (scope) { + builder.where({[EntryColumn.Scope]: scope}); + } + }) + .orderBy('createdAt'); + + if (pageSize) { + relatedEntries.limit(pageSize); + + if (page) { + relatedEntries.offset(pageSize * page); + } + } + + const result = (await relatedEntries.timeout( + extendedTimeout ? EXTENDED_QUERY_TIMEOUT : DEFAULT_QUERY_TIMEOUT, + )) as unknown[] as GetRelatedEntriesResult[]; ctx.log('GET_RELATED_ENTRIES_DONE', { - amount: relatedEntries && relatedEntries.length, + amount: result?.length, }); - return relatedEntries; + return result; } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 3d1fdc54..15bb0edb 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -441,18 +441,6 @@ export class Utils { return dsnList; } - /** @deprecated use getOptimisticNextPageToken */ - static getNextPageToken(page: number, pageSize: number, total: number) { - const lastPage = Math.ceil(total / pageSize) - 1; - let nextPageToken; - - if (page >= 0 && page < lastPage) { - nextPageToken = String(page + 1); - } - - return nextPageToken; - } - static getOptimisticNextPageToken({ page, pageSize,