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 for pagination at table level. (backport #1244) #1538

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 60 additions & 7 deletions indexer/packages/postgres/__tests__/stores/fill-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('Fill store', () => {
FillTable.create(defaultFill),
]);

const fills: FillFromDatabase[] = await FillTable.findAll({}, [], {});
const { results: fills } = await FillTable.findAll({}, [], {});

expect(fills.length).toEqual(2);
expect(fills[0]).toEqual(expect.objectContaining(defaultFill));
Expand All @@ -91,7 +91,7 @@ describe('Fill store', () => {
}),
]);

const fills: FillFromDatabase[] = await FillTable.findAll({}, [], {
const { results: fills } = await FillTable.findAll({}, [], {
orderBy: [[FillColumns.eventId, Ordering.DESC]],
});

Expand All @@ -103,6 +103,59 @@ describe('Fill store', () => {
expect(fills[1]).toEqual(expect.objectContaining(defaultFill));
});

it('Successfully finds fills using pagination', async () => {
await Promise.all([
FillTable.create(defaultFill),
FillTable.create({
...defaultFill,
eventId: defaultTendermintEventId2,
}),
]);

const responsePageOne = await FillTable.findAll({
page: 1,
limit: 1,
}, [], {
orderBy: [[FillColumns.eventId, Ordering.DESC]],
});

expect(responsePageOne.results.length).toEqual(1);
expect(responsePageOne.results[0]).toEqual(expect.objectContaining({
...defaultFill,
eventId: defaultTendermintEventId2,
}));
expect(responsePageOne.offset).toEqual(0);
expect(responsePageOne.total).toEqual(2);

const responsePageTwo = await FillTable.findAll({
page: 2,
limit: 1,
}, [], {
orderBy: [[FillColumns.eventId, Ordering.DESC]],
});

expect(responsePageTwo.results.length).toEqual(1);
expect(responsePageTwo.results[0]).toEqual(expect.objectContaining(defaultFill));
expect(responsePageTwo.offset).toEqual(1);
expect(responsePageTwo.total).toEqual(2);

const responsePageAllPages = await FillTable.findAll({
page: 1,
limit: 2,
}, [], {
orderBy: [[FillColumns.eventId, Ordering.DESC]],
});

expect(responsePageAllPages.results.length).toEqual(2);
expect(responsePageAllPages.results[0]).toEqual(expect.objectContaining({
...defaultFill,
eventId: defaultTendermintEventId2,
}));
expect(responsePageAllPages.results[1]).toEqual(expect.objectContaining(defaultFill));
expect(responsePageAllPages.offset).toEqual(0);
expect(responsePageAllPages.total).toEqual(2);
});

it('Successfully finds Fill with eventId', async () => {
await Promise.all([
FillTable.create(defaultFill),
Expand All @@ -112,7 +165,7 @@ describe('Fill store', () => {
}),
]);

const fills: FillFromDatabase[] = await FillTable.findAll(
const { results: fills } = await FillTable.findAll(
{
eventId: defaultFill.eventId,
},
Expand All @@ -134,7 +187,7 @@ describe('Fill store', () => {
) => {
await FillTable.create(defaultFill);

const fills: FillFromDatabase[] = await FillTable.findAll(
const { results: fills } = await FillTable.findAll(
{
createdBeforeOrAt: createdDateTime.plus({ seconds: deltaSeconds }).toISO(),
},
Expand All @@ -155,7 +208,7 @@ describe('Fill store', () => {
) => {
await FillTable.create(defaultFill);

const fills: FillFromDatabase[] = await FillTable.findAll(
const { results: fills } = await FillTable.findAll(
{
createdBeforeOrAtHeight: Big(createdHeight).plus(deltaBlocks).toFixed(),
},
Expand All @@ -177,7 +230,7 @@ describe('Fill store', () => {
) => {
await FillTable.create(defaultFill);

const fills: FillFromDatabase[] = await FillTable.findAll(
const { results: fills } = await FillTable.findAll(
{
createdOnOrAfter: createdDateTime.minus({ seconds: deltaSeconds }).toISO(),
},
Expand All @@ -199,7 +252,7 @@ describe('Fill store', () => {
) => {
await FillTable.create(defaultFill);

const fills: FillFromDatabase[] = await FillTable.findAll(
const { results: fills } = await FillTable.findAll(
{
createdOnOrAfterHeight: Big(createdHeight).minus(deltaBlocks).toFixed(),
},
Expand Down
82 changes: 74 additions & 8 deletions indexer/packages/postgres/__tests__/stores/order-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
OrderFromDatabase,
Ordering,
OrderStatus,
PaginationFromDatabase,
TimeInForce,
} from '../../src/types';
import * as OrderTable from '../../src/stores/order-table';
Expand Down Expand Up @@ -49,7 +50,9 @@ describe('Order store', () => {
it('Successfully creates an Order with goodTilBlockTime', async () => {
await OrderTable.create(defaultOrderGoodTilBlockTime);

const orders: OrderFromDatabase[] = await OrderTable.findAll({}, [], {});
const {
results: orders,
}: PaginationFromDatabase<OrderFromDatabase> = await OrderTable.findAll({}, [], {});

expect(orders).toHaveLength(1);
expect(orders[0]).toEqual(expect.objectContaining({
Expand All @@ -67,7 +70,9 @@ describe('Order store', () => {
}),
]);

const orders: OrderFromDatabase[] = await OrderTable.findAll({}, [], {
const {
results: orders,
}: PaginationFromDatabase<OrderFromDatabase> = await OrderTable.findAll({}, [], {
orderBy: [[OrderColumns.clientId, Ordering.ASC]],
});

Expand All @@ -79,6 +84,66 @@ describe('Order store', () => {
}));
});

it('Successfully finds all Orders using pagination', async () => {
await Promise.all([
OrderTable.create(defaultOrder),
OrderTable.create({
...defaultOrder,
clientId: '2',
}),
]);

const responsePageOne: PaginationFromDatabase<OrderFromDatabase> = await OrderTable.findAll({
page: 1,
limit: 1,
},
[],
{
orderBy: [[OrderColumns.clientId, Ordering.ASC]],
});

expect(responsePageOne.results.length).toEqual(1);
expect(responsePageOne.results[0]).toEqual(expect.objectContaining(defaultOrder));
expect(responsePageOne.offset).toEqual(0);
expect(responsePageOne.total).toEqual(2);

const responsePageTwo: PaginationFromDatabase<OrderFromDatabase> = await OrderTable.findAll({
page: 2,
limit: 1,
},
[],
{
orderBy: [[OrderColumns.clientId, Ordering.ASC]],
});

expect(responsePageTwo.results.length).toEqual(1);
expect(responsePageTwo.results[0]).toEqual(expect.objectContaining({
...defaultOrder,
clientId: '2',
}));
expect(responsePageTwo.offset).toEqual(1);
expect(responsePageTwo.total).toEqual(2);

const responsePageAllPages: PaginationFromDatabase<OrderFromDatabase> = await OrderTable
.findAll({
page: 1,
limit: 2,
},
[],
{
orderBy: [[OrderColumns.clientId, Ordering.ASC]],
});

expect(responsePageAllPages.results.length).toEqual(2);
expect(responsePageAllPages.results[0]).toEqual(expect.objectContaining(defaultOrder));
expect(responsePageAllPages.results[1]).toEqual(expect.objectContaining({
...defaultOrder,
clientId: '2',
}));
expect(responsePageAllPages.offset).toEqual(0);
expect(responsePageAllPages.total).toEqual(2);
});

it('findOpenLongTermOrConditionalOrders', async () => {
await Promise.all([
OrderTable.create(defaultOrder),
Expand Down Expand Up @@ -106,7 +171,7 @@ describe('Order store', () => {
}),
]);

const orders: OrderFromDatabase[] = await OrderTable.findAll(
const { results: orders }: PaginationFromDatabase<OrderFromDatabase> = await OrderTable.findAll(
{
clientId: '1',
},
Expand Down Expand Up @@ -202,11 +267,12 @@ describe('Order store', () => {
OrderTable.create(defaultOrderGoodTilBlockTime),
]);

const orders: OrderFromDatabase[] = await OrderTable.findAll(
filter,
[],
{ readReplica: true },
);
const { results: orders }: PaginationFromDatabase<OrderFromDatabase> = await OrderTable
.findAll(
filter,
[],
{ readReplica: true },
);

expect(orders).toHaveLength(1);
expect(orders[0]).toEqual(expect.objectContaining(expectedOrder));
Expand Down
57 changes: 54 additions & 3 deletions indexer/packages/postgres/__tests__/stores/transfer-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('Transfer store', () => {
TransferTable.create(transfer2),
]);

const transfers: TransferFromDatabase[] = await TransferTable.findAllToOrFromSubaccountId(
const { results: transfers } = await TransferTable.findAllToOrFromSubaccountId(
{ subaccountId: [defaultSubaccountId] },
[], {
orderBy: [[TransferColumns.id, Ordering.ASC]],
Expand All @@ -142,7 +142,7 @@ describe('Transfer store', () => {
TransferTable.create(transfer2),
]);

const transfers: TransferFromDatabase[] = await TransferTable.findAllToOrFromSubaccountId(
const { results: transfers } = await TransferTable.findAllToOrFromSubaccountId(
{
subaccountId: [defaultSubaccountId],
eventId: [defaultTendermintEventId],
Expand All @@ -155,6 +155,57 @@ describe('Transfer store', () => {
expect(transfers[0]).toEqual(expect.objectContaining(defaultTransfer));
});

it('Successfully finds all transfers to and from subaccount using pagination', async () => {
const transfer2: TransferCreateObject = {
senderSubaccountId: defaultSubaccountId2,
recipientSubaccountId: defaultSubaccountId,
assetId: defaultAsset2.id,
size: '5',
eventId: defaultTendermintEventId2,
transactionHash: '', // TODO: Add a real transaction Hash
createdAt: createdDateTime.toISO(),
createdAtHeight: createdHeight,
};
await Promise.all([
TransferTable.create(defaultTransfer),
TransferTable.create(transfer2),
]);

const responsePageOne = await TransferTable.findAllToOrFromSubaccountId(
{ subaccountId: [defaultSubaccountId], page: 1, limit: 1 },
[], {
orderBy: [[TransferColumns.id, Ordering.ASC]],
});

expect(responsePageOne.results.length).toEqual(1);
expect(responsePageOne.results[0]).toEqual(expect.objectContaining(defaultTransfer));
expect(responsePageOne.offset).toEqual(0);
expect(responsePageOne.total).toEqual(2);

const responsePageTwo = await TransferTable.findAllToOrFromSubaccountId(
{ subaccountId: [defaultSubaccountId], page: 2, limit: 1 },
[], {
orderBy: [[TransferColumns.id, Ordering.ASC]],
});

expect(responsePageTwo.results.length).toEqual(1);
expect(responsePageTwo.results[0]).toEqual(expect.objectContaining(transfer2));
expect(responsePageTwo.offset).toEqual(1);
expect(responsePageTwo.total).toEqual(2);

const responsePageAllPages = await TransferTable.findAllToOrFromSubaccountId(
{ subaccountId: [defaultSubaccountId], page: 1, limit: 2 },
[], {
orderBy: [[TransferColumns.id, Ordering.ASC]],
});

expect(responsePageAllPages.results.length).toEqual(2);
expect(responsePageAllPages.results[0]).toEqual(expect.objectContaining(defaultTransfer));
expect(responsePageAllPages.results[1]).toEqual(expect.objectContaining(transfer2));
expect(responsePageAllPages.offset).toEqual(0);
expect(responsePageAllPages.total).toEqual(2);
});

it('Successfully finds Transfer with eventId', async () => {
await Promise.all([
TransferTable.create(defaultTransfer),
Expand Down Expand Up @@ -234,7 +285,7 @@ describe('Transfer store', () => {
TransferTable.create(transfer2),
]);

const transfers: TransferFromDatabase[] = await TransferTable.findAllToOrFromSubaccountId(
const { results: transfers } = await TransferTable.findAllToOrFromSubaccountId(
{
subaccountId: [defaultSubaccountId],
createdBeforeOrAt: '2000-05-25T00:00:00.000Z',
Expand Down
38 changes: 35 additions & 3 deletions indexer/packages/postgres/src/stores/fill-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
CostOfFills,
QueryableField,
QueryConfig,
PaginationFromDatabase,
} from '../types';

export function uuid(eventId: Buffer, liquidity: Liquidity): string {
Expand All @@ -49,10 +50,11 @@ export async function findAll(
createdOnOrAfter,
clientMetadata,
fee,
page,
}: FillQueryConfig,
requiredFields: QueryableField[],
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<FillFromDatabase[]> {
): Promise<PaginationFromDatabase<FillFromDatabase>> {
verifyAllRequiredFields(
{
limit,
Expand Down Expand Up @@ -156,11 +158,41 @@ export async function findAll(
Ordering.DESC,
);

if (limit !== undefined) {
if (limit !== undefined && page === undefined) {
baseQuery = baseQuery.limit(limit);
}

return baseQuery.returning('*');
/**
* If a query is made using a page number, then the limit property is used as 'page limit'
* TODO: Improve pagination by adding a required eventId for orderBy clause
*/
if (page !== undefined && limit !== undefined) {
/**
* We make sure that the page number is always >= 1
*/
const currentPage: number = Math.max(1, page);
const offset: number = (currentPage - 1) * limit;

/**
* Ensure sorting is applied to maintain consistent pagination results.
* Also a casting of the ts type is required since the infer of the type
* obtained from the count is not performed.
*/
const count: { count?: string } = await baseQuery.clone().clearOrder().count({ count: '*' }).first() as unknown as { count?: string };

baseQuery = baseQuery.offset(offset).limit(limit);

return {
results: await baseQuery.returning('*'),
limit,
offset,
total: parseInt(count.count ?? '0', 10),
};
}

return {
results: await baseQuery.returning('*'),
};
}

export async function create(
Expand Down
Loading
Loading