Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support pagination to getRelations #193

Merged
merged 12 commits into from
Oct 16, 2024
1 change: 1 addition & 0 deletions api/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
4 changes: 4 additions & 0 deletions src/components/response-presenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export async function prepareResponseAsync({data}: {data: any}): Promise<ST.Serv
response.entry = await Utils.macrotasksEncodeData(response.entry);
}

if (response.relations) {
response.relations = await Utils.macrotasksEncodeData(response.relations);
}

return {
code: 200,
response,
Expand Down
9 changes: 9 additions & 0 deletions src/const/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {EntryScope} from '../db/models/new/entry/types';
import {Mode} from '../types/models';

export const TRUE_FLAGS = ['1', 'true', true];
Expand Down Expand Up @@ -244,3 +245,11 @@ export const INTER_TENANT_GET_ENTRIES_SCHEMA = {
};

export const ModeValues: Mode[] = ['save', 'publish'];

export const ALLOWED_ENTRIES_SCOPE = [
EntryScope.Dash,
EntryScope.Widget,
EntryScope.Dataset,
EntryScope.Connection,
EntryScope.Report,
] as const;
12 changes: 11 additions & 1 deletion src/controllers/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
formatGetEntryMetaPrivateResponse,
formatEntryModel,
} from '../services/new/entry/formatters';
import {EntryScope} from '../db/models/new/entry/types';

export default {
getEntry: async (req: Request, res: Response) => {
Expand Down Expand Up @@ -243,10 +244,19 @@ export default {
entryId: params.entryId,
direction: query.direction as Optional<RelationDirection>,
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);
},
Expand Down
51 changes: 48 additions & 3 deletions src/services/entry/actions/get-entry-relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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,
},
},
});

Expand All @@ -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,
});

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -138,5 +180,8 @@ export async function getEntryRelations(

ctx.log('GET_ENTRY_RELATIONS_SUCCESS', {count: relations.length});

return relations;
return {
relations,
nextPageToken,
};
}
37 changes: 29 additions & 8 deletions src/services/entry/actions/get-related-entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -18,6 +19,9 @@ type GetRelatedEntriesData = {
entryIds: string[];
direction?: RelationDirection;
extendedTimeout?: boolean;
scope?: EntryScope;
page?: number;
pageSize?: number;
};

type GetRelatedEntriesResult = {
Expand All @@ -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')
Expand Down Expand Up @@ -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;
}
12 changes: 0 additions & 12 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading