@@ -203,6 +203,7 @@ export interface QueryEntitiesInitialRequest {
|
||||
term: string;
|
||||
fields?: string[];
|
||||
};
|
||||
skipTotalItems?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1478,6 +1478,52 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
},
|
||||
);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can skip totalItems, %p',
|
||||
async databaseId => {
|
||||
await createDatabase(databaseId);
|
||||
|
||||
await Promise.all(
|
||||
Array(15)
|
||||
.fill(0)
|
||||
.map(() =>
|
||||
addEntityToSearch({
|
||||
apiVersion: 'a',
|
||||
kind: 'k',
|
||||
metadata: { name: v4() },
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const catalog = new DefaultEntitiesCatalog({
|
||||
database: knex,
|
||||
logger: mockServices.logger.mock(),
|
||||
stitcher,
|
||||
});
|
||||
|
||||
const request: QueryEntitiesInitialRequest = {
|
||||
limit: 10,
|
||||
credentials: mockCredentials.none(),
|
||||
skipTotalItems: true,
|
||||
};
|
||||
let response = await catalog.queryEntities(request);
|
||||
expect(response).toEqual({
|
||||
totalItems: 0,
|
||||
items: expect.objectContaining({ length: 10 }),
|
||||
pageInfo: { nextCursor: expect.anything() },
|
||||
});
|
||||
response = await catalog.queryEntities({
|
||||
...request,
|
||||
cursor: response.pageInfo.nextCursor!,
|
||||
});
|
||||
expect(response).toEqual({
|
||||
totalItems: 0,
|
||||
items: expect.objectContaining({ length: 5 }),
|
||||
pageInfo: { prevCursor: expect.anything() },
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'should paginate results accordingly in case of clashing items, %p',
|
||||
async databaseId => {
|
||||
|
||||
@@ -331,6 +331,7 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog {
|
||||
|
||||
const cursor: Omit<Cursor, 'orderFieldValues'> & {
|
||||
orderFieldValues?: (string | null)[];
|
||||
skipTotalItems: boolean;
|
||||
} = {
|
||||
orderFields: [],
|
||||
isPrevious: false,
|
||||
@@ -341,7 +342,8 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog {
|
||||
// request. The result is then embedded into the cursor for subsequent
|
||||
// requests. Threfore this can be undefined here, but will then get
|
||||
// populated further down.
|
||||
const shouldComputeTotalItems = cursor.totalItems === undefined;
|
||||
const shouldComputeTotalItems =
|
||||
cursor.totalItems === undefined && !cursor.skipTotalItems;
|
||||
const isFetchingBackwards = cursor.isPrevious;
|
||||
|
||||
if (cursor.orderFields.length > 1) {
|
||||
@@ -532,8 +534,16 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog {
|
||||
|
||||
const rows = shouldComputeTotalItems || limit > 0 ? await dbQuery : [];
|
||||
|
||||
const totalItems =
|
||||
cursor.totalItems ?? (rows.length ? Number(rows[0].count) : 0);
|
||||
let totalItems: number;
|
||||
if (cursor.totalItems !== undefined) {
|
||||
totalItems = cursor.totalItems;
|
||||
} else if (cursor.skipTotalItems) {
|
||||
totalItems = 0;
|
||||
} else if (rows.length) {
|
||||
totalItems = Number(rows[0].count);
|
||||
} else {
|
||||
totalItems = 0;
|
||||
}
|
||||
|
||||
if (isFetchingBackwards) {
|
||||
rows.reverse();
|
||||
@@ -809,15 +819,26 @@ export const cursorParser: z.ZodSchema<Cursor> = z.object({
|
||||
|
||||
function parseCursorFromRequest(
|
||||
request?: QueryEntitiesRequest,
|
||||
): Partial<Cursor> {
|
||||
): Partial<Cursor> & { skipTotalItems: boolean } {
|
||||
if (isQueryEntitiesInitialRequest(request)) {
|
||||
const { filter, orderFields: sortFields = [], fullTextFilter } = request;
|
||||
return { filter, orderFields: sortFields, fullTextFilter };
|
||||
const {
|
||||
filter,
|
||||
orderFields: sortFields = [],
|
||||
fullTextFilter,
|
||||
skipTotalItems = false,
|
||||
} = request;
|
||||
return { filter, orderFields: sortFields, fullTextFilter, skipTotalItems };
|
||||
}
|
||||
if (isQueryEntitiesCursorRequest(request)) {
|
||||
return request.cursor;
|
||||
return {
|
||||
...request.cursor,
|
||||
// Doesn't matter here
|
||||
skipTotalItems: false,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
return {
|
||||
skipTotalItems: false,
|
||||
};
|
||||
}
|
||||
|
||||
function invertOrder(order: EntityOrder['order']) {
|
||||
|
||||
@@ -173,6 +173,7 @@ describe('createRouter readonly disabled', () => {
|
||||
},
|
||||
limit: 10000,
|
||||
credentials: mockCredentials.user(),
|
||||
skipTotalItems: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -192,7 +192,14 @@ export async function createRouter(
|
||||
do {
|
||||
const result = await entitiesCatalog.queryEntities(
|
||||
!cursor
|
||||
? { credentials, fields, limit, filter, orderFields: order }
|
||||
? {
|
||||
credentials,
|
||||
fields,
|
||||
limit,
|
||||
filter,
|
||||
orderFields: order,
|
||||
skipTotalItems: true,
|
||||
}
|
||||
: { credentials, fields, limit, cursor },
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user