🐛 Fixing pagination for "Shared with me"/ES (#778)

This commit is contained in:
Montassar Ghanmy
2025-01-08 17:58:01 +01:00
committed by GitHub
parent f04e5f0535
commit fd0ff703b6
10 changed files with 78 additions and 16 deletions
@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types */
import { SearchDocumentsOptions } from "src/services/documents/types";
import { TdriveServiceProvider } from "../../framework"; import { TdriveServiceProvider } from "../../framework";
import { ListResult } from "../../framework/api/crud-service"; import { ListResult } from "../../framework/api/crud-service";
import { import {
@@ -62,6 +63,7 @@ export interface SearchServiceAPI extends TdriveServiceProvider {
getRepository<Entity>(table: string, entityType: EntityTarget<Entity>): SearchRepository<Entity>; getRepository<Entity>(table: string, entityType: EntityTarget<Entity>): SearchRepository<Entity>;
upsert(entities: any[]): Promise<void>; upsert(entities: any[]): Promise<void>;
remove(entities: any[]): Promise<void>; remove(entities: any[]): Promise<void>;
handlePagination(options: SearchDocumentsOptions): void;
type: SearchConfiguration["type"]; type: SearchConfiguration["type"];
} }
@@ -15,6 +15,7 @@ import { DatabaseServiceAPI } from "../database/api";
import SearchRepository from "./repository"; import SearchRepository from "./repository";
import { Client as OpenClient } from "@opensearch-project/opensearch"; import { Client as OpenClient } from "@opensearch-project/opensearch";
import { Client as ESClient } from "@elastic/elasticsearch"; import { Client as ESClient } from "@elastic/elasticsearch";
import { SearchDocumentsOptions } from "src/services/documents/types";
@ServiceName("search") @ServiceName("search")
@Consumes(["database"]) @Consumes(["database"])
@@ -96,4 +97,30 @@ export default class Search extends TdriveService<SearchServiceAPI> {
api(): SearchServiceAPI { api(): SearchServiceAPI {
return this; return this;
} }
handlePagination(options: SearchDocumentsOptions): void {
if (this.type === "mongodb") {
// No custom pagination logic for MongoDB
return;
}
if (this.type === "elasticsearch" || this.type === "opensearch") {
if (options.nextPage?.page_token) {
// Set the scroll ID as the page token
options.pagination = {
...options.pagination,
page_token: options.nextPage.page_token,
};
} else {
// Clear pagination if no nextPage token is provided
options.pagination = {
limitStr: options.pagination?.limitStr,
};
}
return;
}
// Handle unsupported platform types
throw new Error(`Pagination handling is not implemented for platform type: ${this.type}`);
}
} }
@@ -133,6 +133,9 @@ export class DocumentsService {
options.sort = this.getSortFieldMapping(options.sort); options.sort = this.getSortFieldMapping(options.sort);
} }
// Handle pagination differently for non-MongoDB platforms
globalResolver.platformServices.search.handlePagination(options);
const fileList: ListResult<DriveFile> = await this.search(options, context); const fileList: ListResult<DriveFile> = await this.search(options, context);
const result = fileList.getEntities(); const result = fileList.getEntities();
@@ -57,12 +57,18 @@ export type SearchDocumentsOptions = {
onlyDirectlyShared?: boolean; onlyDirectlyShared?: boolean;
onlyUploadedNotByMe?: boolean; onlyUploadedNotByMe?: boolean;
pagination?: Paginable; pagination?: Paginable;
nextPage?: {
page_token: string;
};
}; };
export type BrowseDocumentsOptions = { export type BrowseDocumentsOptions = {
filter?: SearchDocumentsBody; filter?: SearchDocumentsBody;
sort?: SortType; sort?: SortType;
paginate?: Paginable; paginate?: Paginable;
nextPage?: {
page_token: string;
};
}; };
export type SearchDocumentsBody = { export type SearchDocumentsBody = {
@@ -213,6 +213,7 @@ export class DocumentsController {
onlyUploadedNotByMe: true, onlyUploadedNotByMe: true,
sort: request.body.sort, sort: request.body.sort,
pagination: request.body.paginate, pagination: request.body.paginate,
nextPage: request.body.nextPage,
}; };
return { return {
@@ -70,7 +70,13 @@ export class DriveApiClient {
); );
} }
static async browse(companyId: string, id: string | 'trash' | '', filter: BrowseFilter, sort: BrowseSort, paginate: BrowsePaginate) { static async browse(
companyId: string,
id: string | 'trash' | '',
filter: BrowseFilter,
sort: BrowseSort,
paginate: BrowsePaginate,
) {
return await Api.post<BrowseQuery, DriveItemDetails>( return await Api.post<BrowseQuery, DriveItemDetails>(
`/internal/services/documents/v1/companies/${companyId}/browse/${id}${appendTdriveToken()}`, `/internal/services/documents/v1/companies/${companyId}/browse/${id}${appendTdriveToken()}`,
{ {
@@ -79,7 +85,10 @@ export class DriveApiClient {
paginate: { paginate: {
page_token: paginate.page.toString(), page_token: paginate.page.toString(),
limitStr: paginate.limit.toString(), limitStr: paginate.limit.toString(),
} },
nextPage: {
page_token: paginate.nextPage?.page_token || '',
},
}, },
); );
} }
@@ -93,7 +102,7 @@ export class DriveApiClient {
static async restore(companyId: string, id: string | 'trash' | '') { static async restore(companyId: string, id: string | 'trash' | '') {
return await Api.post<any, any>( return await Api.post<any, any>(
`/internal/services/documents/v1/companies/${companyId}/item/${id}/restore${appendTdriveToken()}`, `/internal/services/documents/v1/companies/${companyId}/item/${id}/restore${appendTdriveToken()}`,
{} {},
); );
} }
@@ -111,13 +120,10 @@ export class DriveApiClient {
if (!data.version) data.version = {} as Partial<DriveItemVersion>; if (!data.version) data.version = {} as Partial<DriveItemVersion>;
return new Promise<DriveItem>((resolve, reject) => { return new Promise<DriveItem>((resolve, reject) => {
Api.post< Api.post<{ item: Partial<DriveItem>; version: Partial<DriveItemVersion> }, DriveItem>(
{ item: Partial<DriveItem>; version: Partial<DriveItemVersion> },
DriveItem
>(
`/internal/services/documents/v1/companies/${companyId}/item${appendTdriveToken()}`, `/internal/services/documents/v1/companies/${companyId}/item${appendTdriveToken()}`,
data as { item: Partial<DriveItem>; version: Partial<DriveItemVersion> }, data as { item: Partial<DriveItem>; version: Partial<DriveItemVersion> },
(res) => { res => {
if ((res as any)?.statusCode || (res as any)?.error) { if ((res as any)?.statusCode || (res as any)?.error) {
reject(res); reject(res);
} }
@@ -151,7 +157,9 @@ export class DriveApiClient {
static getDownloadUrl(companyId: string, id: string, versionId?: string) { static getDownloadUrl(companyId: string, id: string, versionId?: string) {
if (versionId) if (versionId)
return Api.route(`/internal/services/documents/v1/companies/${companyId}/item/${id}/download?version_id=${versionId}`); return Api.route(
`/internal/services/documents/v1/companies/${companyId}/item/${id}/download?version_id=${versionId}`,
);
return Api.route(`/internal/services/documents/v1/companies/${companyId}/item/${id}/download`); return Api.route(`/internal/services/documents/v1/companies/${companyId}/item/${id}/download`);
} }
@@ -9,7 +9,7 @@ import {
DriveItemPagination, DriveItemPagination,
DriveItemSort, DriveItemSort,
} from '../state/store'; } from '../state/store';
import { BrowseFilter, DriveItem, DriveItemVersion } from '../types'; import { BrowseFilter, DriveItem, DriveItemDetails, DriveItemVersion } from '../types';
import { SharedWithMeFilterState } from '../state/shared-with-me-filter'; import { SharedWithMeFilterState } from '../state/shared-with-me-filter';
import Languages from 'features/global/services/languages-service'; import Languages from 'features/global/services/languages-service';
import { useUserQuota } from 'features/users/hooks/use-user-quota'; import { useUserQuota } from 'features/users/hooks/use-user-quota';
@@ -70,11 +70,12 @@ export const useDriveActions = (inPublicSharing?: boolean) => {
let pagination = await snapshot.getPromise(DriveItemPagination); let pagination = await snapshot.getPromise(DriveItemPagination);
if (resetPagination) { if (resetPagination) {
pagination = { page: 0, limit: pagination.limit }; pagination = { page: 0, limit: pagination.limit, nextPage: pagination.nextPage };
set(DriveItemPagination, pagination); set(DriveItemPagination, pagination);
} }
let details: DriveItemDetails | undefined;
try { try {
const details = await DriveApiClient.browse( details = await DriveApiClient.browse(
companyId, companyId,
parentId, parentId,
filter, filter,
@@ -94,7 +95,13 @@ export const useDriveActions = (inPublicSharing?: boolean) => {
} catch (e) { } catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_load_file')); ToasterService.error(Languages.t('hooks.use-drive-actions.unable_load_file'));
} finally { } finally {
set(DriveItemPagination, { page: pagination.limit, limit: pagination.limit }); set(DriveItemPagination, {
page: pagination.limit,
limit: pagination.limit,
nextPage: {
page_token: details?.nextPage?.page_token || '',
},
});
} }
} }
}, },
@@ -11,9 +11,9 @@ export const DriveItemAtom = atomFamily<Partial<DriveItemDetails> | null, string
default: () => null, default: () => null,
}); });
export const DriveItemSelectedList = atom<{[key: string]: boolean }>({ export const DriveItemSelectedList = atom<{ [key: string]: boolean }>({
key: 'DriveItemSelectedList', key: 'DriveItemSelectedList',
default: {} default: {},
}); });
export const DriveItemSort = atom<BrowseSort>({ export const DriveItemSort = atom<BrowseSort>({
@@ -5,6 +5,9 @@ export type BrowseQuery = {
page_token: string; page_token: string;
limitStr: string; limitStr: string;
}; };
nextPage?: {
page_token: string;
};
} }
export type BrowseFilter = { export type BrowseFilter = {
@@ -21,6 +24,9 @@ export type BrowsePaginate = {
page: number; page: number;
limit: number; limit: number;
lastPage?: boolean; lastPage?: boolean;
nextPage?: {
page_token: string;
};
}; };
export type DriveItemDetails = { export type DriveItemDetails = {
@@ -33,6 +39,9 @@ export type DriveItemDetails = {
room: string; room: string;
token?: string; token?: string;
}[]; }[];
nextPage?: {
page_token: string;
}
}; };
export type DriveItem = { export type DriveItem = {
@@ -137,7 +137,6 @@ export default memo(
useEffect(() => { useEffect(() => {
setChecked({}); setChecked({});
refresh(parentId); refresh(parentId);
if (!inPublicSharing) refresh('trash');
}, [parentId, refresh, filter]); }, [parentId, refresh, filter]);
const uploadItemModal = useCallback(() => { const uploadItemModal = useCallback(() => {