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

feat: pagination #1490

Merged
merged 32 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b38226e
feat: pagination
rodrigopavezi Nov 19, 2024
fdd7f5f
fix: first and skip
rodrigopavezi Nov 19, 2024
378c229
fix: Number conversion issue
rodrigopavezi Nov 19, 2024
5857faf
fix: pagination validation before subgraph query
rodrigopavezi Nov 19, 2024
2cb1afe
fix: per coderabitai review
rodrigopavezi Nov 19, 2024
a0e320c
fix: per coderabitai review second time
rodrigopavezi Nov 19, 2024
3e05e00
fix: as per coderabitai review for the third time
rodrigopavezi Nov 19, 2024
30504a1
fix: as per coderabitai review for the fourth time
rodrigopavezi Nov 19, 2024
70b4696
fix: build
rodrigopavezi Nov 19, 2024
2d6e838
fix: as per coderabitai for the sixth time
rodrigopavezi Nov 19, 2024
99befe6
fix: duplicate types
rodrigopavezi Nov 19, 2024
bbc876f
remove check for pagination
rodrigopavezi Nov 19, 2024
be0090b
fix: page and pageSize params
rodrigopavezi Nov 19, 2024
db40d41
fix: transaction manager tests
rodrigopavezi Nov 19, 2024
3be1003
fix: request-node failing test
rodrigopavezi Nov 20, 2024
e22ab74
test: with Number instead of parseInt
rodrigopavezi Nov 20, 2024
9b85288
debug: add more logging
rodrigopavezi Nov 20, 2024
e3b26b1
fix: min pagination for pending items
rodrigopavezi Nov 20, 2024
7558799
refactor: getChannelsByMultipleTopics with pagination
rodrigopavezi Nov 20, 2024
20ac8a3
fix: adjustedPageSize equal zero
rodrigopavezi Nov 20, 2024
6baba83
fix: enhance coding and logic
rodrigopavezi Nov 20, 2024
e88c205
fix: integration test
rodrigopavezi Nov 20, 2024
d04c257
revert changes
rodrigopavezi Nov 20, 2024
ef4ef09
fix: getChannelsByMultipleTopics pagination
rodrigopavezi Nov 20, 2024
385d74e
fix: request node test
rodrigopavezi Nov 20, 2024
c4de059
reverted
rodrigopavezi Nov 20, 2024
88aa1fa
fix: issue
rodrigopavezi Nov 20, 2024
8f97941
Merge branch 'master' into feat/pagination
rodrigopavezi Nov 26, 2024
e0b2b82
fix: pagination reverting some code changes
rodrigopavezi Nov 26, 2024
3dfc31c
fix: per coderabitai reviews
rodrigopavezi Nov 26, 2024
69aa757
Update packages/transaction-manager/test/index.test.ts
rodrigopavezi Nov 26, 2024
5fd6588
fix: tests
rodrigopavezi Nov 26, 2024
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
8 changes: 6 additions & 2 deletions packages/data-access/src/combined-data-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ export abstract class CombinedDataAccess implements DataAccessTypes.IDataAccess
async getChannelsByTopic(
topic: string,
updatedBetween?: DataAccessTypes.ITimestampBoundaries | undefined,
page?: number | undefined,
pageSize?: number | undefined,
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
return await this.reader.getChannelsByTopic(topic, updatedBetween);
return await this.reader.getChannelsByTopic(topic, updatedBetween, page, pageSize);
}
async getChannelsByMultipleTopics(
topics: string[],
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
page?: number | undefined,
pageSize?: number | undefined,
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
return await this.reader.getChannelsByMultipleTopics(topics, updatedBetween);
return await this.reader.getChannelsByMultipleTopics(topics, updatedBetween, page, pageSize);
}
async persistTransaction(
transactionData: DataAccessTypes.ITransaction,
Expand Down
90 changes: 68 additions & 22 deletions packages/data-access/src/data-read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,28 @@ export class DataAccessRead implements DataAccessTypes.IDataRead {
async getChannelsByTopic(
topic: string,
updatedBetween?: DataAccessTypes.ITimestampBoundaries | undefined,
page?: number | undefined,
pageSize?: number | undefined,
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
return this.getChannelsByMultipleTopics([topic], updatedBetween);
return this.getChannelsByMultipleTopics([topic], updatedBetween, page, pageSize);
}

async getChannelsByMultipleTopics(
topics: string[],
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
page?: number,
pageSize?: number,
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
const result = await this.storage.getTransactionsByTopics(topics);
const pending = this.pendingStore?.findByTopics(topics) || [];
// Validate pagination parameters
if (page !== undefined && page < 1) {
throw new Error('Page number must be greater than or equal to 1');
}
if (pageSize !== undefined && pageSize <= 0) {
throw new Error('Page size must be positive');
}

// Get pending items first
const pending = this.pendingStore?.findByTopics(topics) || [];
const pendingItems = pending.map((item) => ({
hash: item.storageResult.id,
channelId: item.channelId,
Expand All @@ -73,20 +84,55 @@ export class DataAccessRead implements DataAccessTypes.IDataRead {
topics: item.topics || [],
}));

const transactions = result.transactions.concat(...pendingItems);
// Adjust pagination to account for pending items
let adjustedPage = page;
let adjustedPageSize = pageSize;
if (page !== undefined && pageSize !== undefined) {
if (pendingItems.length >= (page - 1) * pageSize) {
// If pending items fill previous pages
adjustedPage = 0;
adjustedPageSize = 0;
} else {
// Adjust page size to account for pending items included
const pendingItemsInPreviousPages = Math.min(pendingItems.length, (page - 1) * pageSize);
const pendingItemsInCurrentPage = Math.min(
pendingItems.length - pendingItemsInPreviousPages,
pageSize,
);
adjustedPageSize = pageSize - pendingItemsInCurrentPage;
adjustedPage = Math.floor(
((page - 1) * pageSize - pendingItemsInPreviousPages) / adjustedPageSize,
);
}
}

// list of channels having at least one tx updated during the updatedBetween boundaries
const channels = (
updatedBetween
? transactions.filter(
(tx) =>
tx.blockTimestamp >= (updatedBetween.from || 0) &&
tx.blockTimestamp <= (updatedBetween.to || Number.MAX_SAFE_INTEGER),
)
: transactions
).map((x) => x.channelId);
// Fetch transactions from storage with adjusted pagination
const result = await this.storage.getTransactionsByTopics(
topics,
adjustedPage,
adjustedPageSize,
);

// Combine pending and stored transactions
const transactions = [...pendingItems, ...result.transactions];

// Proceed with filtering and mapping as per existing logic
const filteredTxs = transactions.filter((tx) => {
if (!updatedBetween) return true;
return (
tx.blockTimestamp >= (updatedBetween.from || 0) &&
tx.blockTimestamp <= (updatedBetween.to || Number.MAX_SAFE_INTEGER)
);
});

const finalTransactions = filteredTxs.reduce((prev, curr) => {
if (!prev[curr.channelId]) {
prev[curr.channelId] = [];
}
prev[curr.channelId].push(this.toTimestampedTransaction(curr));
return prev;
}, {} as DataAccessTypes.ITransactionsByChannelIds);

const filteredTxs = transactions.filter((tx) => channels.includes(tx.channelId));
return {
meta: {
storageMeta: filteredTxs.reduce(
Expand All @@ -106,15 +152,15 @@ export class DataAccessRead implements DataAccessTypes.IDataRead {
},
{} as Record<string, string[]>,
),
pagination: {
page: page,
pageSize: pageSize,
total: filteredTxs.length,
hasMore: filteredTxs.length > (page || 0) * (pageSize || 0),
},
rodrigopavezi marked this conversation as resolved.
Show resolved Hide resolved
},
result: {
transactions: filteredTxs.reduce((prev, curr) => {
if (!prev[curr.channelId]) {
prev[curr.channelId] = [];
}
prev[curr.channelId].push(this.toTimestampedTransaction(curr));
return prev;
}, {} as DataAccessTypes.ITransactionsByChannelIds),
transactions: finalTransactions,
},
};
}
Expand Down
34 changes: 32 additions & 2 deletions packages/data-access/src/in-memory-indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,38 @@ export class InMemoryIndexer implements StorageTypes.IIndexer {
};
}

async getTransactionsByTopics(topics: string[]): Promise<StorageTypes.IGetTransactionsResponse> {
const channelIds = topics.map((topic) => this.#topicToChannelsIndex.get(topic)).flat();
async getTransactionsByTopics(
topics: string[],
page?: number,
pageSize?: number,
): Promise<StorageTypes.IGetTransactionsResponse> {
if (page !== undefined && page < 1) {
throw new Error('Page must be greater than or equal to 1');
}
if (pageSize !== undefined && pageSize <= 0) {
throw new Error('Page size must be greater than 0');
}

// Efficiently get total count without creating intermediate array
const channelIdsSet = new Set(topics.flatMap((topic) => this.#topicToChannelsIndex.get(topic)));
const total = channelIdsSet.size;
let channelIds = Array.from(channelIdsSet);

if (page && pageSize) {
const start = (page - 1) * pageSize;
// Return empty result if page exceeds available data
if (start >= total) {
return {
blockNumber: 0,
transactions: [],
pagination:
page && pageSize
? { total, page, pageSize, hasMore: page * pageSize < total }
: undefined,
};
}
channelIds = channelIds.slice(start, start + pageSize);
}
const locations = channelIds
.map((channel) => this.#channelToLocationsIndex.get(channel))
.flat();
Expand Down
42 changes: 36 additions & 6 deletions packages/request-client.js/src/api/request-network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,12 @@ export default class RequestNetwork {
public async fromIdentity(
identity: IdentityTypes.IIdentity,
updatedBetween?: Types.ITimestampBoundaries,
options?: { disablePaymentDetection?: boolean; disableEvents?: boolean },
options?: {
disablePaymentDetection?: boolean;
disableEvents?: boolean;
page?: number;
pageSize?: number;
},
): Promise<Request[]> {
if (!this.supportedIdentities.includes(identity.type)) {
throw new Error(`${identity.type} is not supported`);
Expand All @@ -283,7 +288,12 @@ export default class RequestNetwork {
public async fromMultipleIdentities(
identities: IdentityTypes.IIdentity[],
updatedBetween?: Types.ITimestampBoundaries,
options?: { disablePaymentDetection?: boolean; disableEvents?: boolean },
options?: {
disablePaymentDetection?: boolean;
disableEvents?: boolean;
page?: number;
pageSize?: number;
},
): Promise<Request[]> {
const identityNotSupported = identities.find(
(identity) => !this.supportedIdentities.includes(identity.type),
Expand All @@ -306,11 +316,21 @@ export default class RequestNetwork {
public async fromTopic(
topic: any,
updatedBetween?: Types.ITimestampBoundaries,
options?: { disablePaymentDetection?: boolean; disableEvents?: boolean },
options?: {
disablePaymentDetection?: boolean;
disableEvents?: boolean;
page?: number;
pageSize?: number;
},
): Promise<Request[]> {
// Gets all the requests indexed by the value of the identity
const requestsAndMeta: RequestLogicTypes.IReturnGetRequestsByTopic =
await this.requestLogic.getRequestsByTopic(topic, updatedBetween);
await this.requestLogic.getRequestsByTopic(
topic,
updatedBetween,
options?.page,
options?.pageSize,
);
rodrigopavezi marked this conversation as resolved.
Show resolved Hide resolved
// From the requests of the request-logic layer creates the request objects and gets the payment networks
const requestPromises = requestsAndMeta.result.requests.map(
async (requestFromLogic: {
Expand Down Expand Up @@ -358,11 +378,21 @@ export default class RequestNetwork {
public async fromMultipleTopics(
topics: any[],
updatedBetween?: Types.ITimestampBoundaries,
options?: { disablePaymentDetection?: boolean; disableEvents?: boolean },
options?: {
disablePaymentDetection?: boolean;
disableEvents?: boolean;
page?: number;
pageSize?: number;
},
): Promise<Request[]> {
// Gets all the requests indexed by the value of the identity
const requestsAndMeta: RequestLogicTypes.IReturnGetRequestsByTopic =
await this.requestLogic.getRequestsByMultipleTopics(topics, updatedBetween);
await this.requestLogic.getRequestsByMultipleTopics(
topics,
updatedBetween,
options?.page,
options?.pageSize,
);
rodrigopavezi marked this conversation as resolved.
Show resolved Hide resolved

// From the requests of the request-logic layer creates the request objects and gets the payment networks
const requestPromises = requestsAndMeta.result.requests.map(
Expand Down
15 changes: 15 additions & 0 deletions packages/request-client.js/src/http-data-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,21 @@ export default class HttpDataAccess implements DataAccessTypes.IDataAccess {
public async getChannelsByTopic(
topic: string,
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
page?: number,
pageSize?: number,
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
if (page !== undefined && page < 1) {
throw new Error('Page must be greater than or equal to 1');
}
if (pageSize !== undefined && pageSize <= 0) {
throw new Error('Page size must be greater than 0');
}

return await this.fetchAndRetry('/getChannelsByTopic', {
topic,
updatedBetween,
page,
pageSize,
});
}

Expand All @@ -191,10 +202,14 @@ export default class HttpDataAccess implements DataAccessTypes.IDataAccess {
public async getChannelsByMultipleTopics(
topics: string[],
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
page?: number,
pageSize?: number,
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
return await this.fetchAndRetry('/getChannelsByMultipleTopics', {
topics,
updatedBetween,
page,
pageSize,
rodrigopavezi marked this conversation as resolved.
Show resolved Hide resolved
rodrigopavezi marked this conversation as resolved.
Show resolved Hide resolved
});
}

Expand Down
34 changes: 28 additions & 6 deletions packages/request-client.js/test/api/request-network.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ describe('api/request-network', () => {
it('can get requests with payment network fromIdentity', async () => {
const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = {
...mockDataAccess,
async getChannelsByTopic(topic: string): Promise<any> {
async getChannelsByTopic(
topic: string,
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
page?: number,
pageSize?: number,
): Promise<any> {
expect(topic).toBe('01f1a21ab419611dbf492b3136ac231c8773dc897ee0eb5167ef2051a39e685e76');
return {
meta: {
Expand Down Expand Up @@ -137,7 +142,14 @@ describe('api/request-network', () => {
};

const requestnetwork = new RequestNetwork({ dataAccess: mockDataAccessWithTxs });
const requests: Request[] = await requestnetwork.fromIdentity(TestData.payee.identity);
const requests: Request[] = await requestnetwork.fromIdentity(
TestData.payee.identity,
undefined,
{
page: 1,
pageSize: 10,
},
);

expect(requests.length).toBe(2);
expect(requests[0].requestId).toBe(TestData.actionRequestId);
Expand Down Expand Up @@ -201,7 +213,12 @@ describe('api/request-network', () => {
it('can get requests with payment network from multiple Identities', async () => {
const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = {
...mockDataAccess,
async getChannelsByMultipleTopics(topics: [string]): Promise<any> {
async getChannelsByMultipleTopics(
topics: [string],
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
page?: number,
pageSize?: number,
): Promise<any> {
expect(topics).toEqual([
'01f1a21ab419611dbf492b3136ac231c8773dc897ee0eb5167ef2051a39e685e76',
]);
Expand Down Expand Up @@ -245,9 +262,14 @@ describe('api/request-network', () => {
};

const requestnetwork = new RequestNetwork({ dataAccess: mockDataAccessWithTxs });
const requests: Request[] = await requestnetwork.fromMultipleIdentities([
TestData.payee.identity,
]);
const requests: Request[] = await requestnetwork.fromMultipleIdentities(
[TestData.payee.identity],
undefined,
{
page: 1,
pageSize: 10,
},
);

expect(requests.length).toBe(2);
expect(requests[0].requestId).toBe(TestData.actionRequestId);
Expand Down
8 changes: 8 additions & 0 deletions packages/request-logic/src/request-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,13 +346,17 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic {
public async getRequestsByTopic(
topic: string,
updatedBetween?: RequestLogicTypes.ITimestampBoundaries,
page?: number,
pageSize?: number,
): Promise<RequestLogicTypes.IReturnGetRequestsByTopic> {
// hash all the topics
const hashedTopic = MultiFormat.serialize(normalizeKeccak256Hash(topic));

const getChannelsResult = await this.transactionManager.getChannelsByTopic(
hashedTopic,
updatedBetween,
page,
pageSize,
);
return this.computeMultipleRequestFromChannels(getChannelsResult);
}
Expand All @@ -365,6 +369,8 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic {
public async getRequestsByMultipleTopics(
topics: string[],
updatedBetween?: RequestLogicTypes.ITimestampBoundaries,
page?: number,
pageSize?: number,
): Promise<RequestLogicTypes.IReturnGetRequestsByTopic> {
// hash all the topics
const hashedTopics = topics.map((topic) =>
Expand All @@ -374,6 +380,8 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic {
const getChannelsResult = await this.transactionManager.getChannelsByMultipleTopics(
hashedTopics,
updatedBetween,
page,
pageSize,
);
return this.computeMultipleRequestFromChannels(getChannelsResult);
}
Expand Down
Loading