Skip to content

Commit b345df8

Browse files
authored
feat: add getChannelByTopic in data-access (#305)
**Data-access**: * add getChannelByTopic **Transaction manager**: * add getChannelByTopic **Request logic**: * integrate getChannelByTopic **Request node**: * add getChannelByTopic **Request client**: * integrate getChannelByTopic
1 parent 6072905 commit b345df8

File tree

16 files changed

+445
-16
lines changed

16 files changed

+445
-16
lines changed

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"tx",
6060
"txs",
6161
"tsconfig",
62+
"typedef",
6263
"typeof",
6364
"utils",
6465
"validator",

packages/data-access/src/data-access.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,77 @@ export default class DataAccess implements DataAccessTypes.IDataAccess {
271271
);
272272
}
273273

274+
/**
275+
* Function to get a list of channels indexed by topic
276+
*
277+
* @param topic topic to retrieve the transaction from
278+
* @param updatedBetween filter the channels that have received new data within the time boundaries
279+
*
280+
* @returns list of channels indexed by topic
281+
*/
282+
public async getChannelsByTopic(
283+
topic: string,
284+
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
285+
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
286+
if (!this.locationByTopic) {
287+
throw new Error('DataAccess must be initialized');
288+
}
289+
290+
// Gets the list of locationStorage grouped by channel id for the topic given
291+
const storageLocationByChannelId = this.locationByTopic.getStorageLocationFromTopicGroupedByChannelId(
292+
topic,
293+
);
294+
295+
let channelIds = Object.keys(storageLocationByChannelId);
296+
297+
// Filters the channels to only keep the modified ones during the time boundaries
298+
if (updatedBetween) {
299+
channelIds = channelIds.filter(channelId => {
300+
return storageLocationByChannelId[channelId].find(dataId =>
301+
this.timestampByLocation.isDataInBoundaries(dataId, updatedBetween),
302+
);
303+
});
304+
}
305+
306+
// Gets the transactions per channel id
307+
const transactionsAndMeta = await Promise.all(
308+
channelIds.map(channelId =>
309+
this.getTransactionsByChannelId(channelId).then(transactionsWithMeta => ({
310+
channelId,
311+
transactionsWithMeta,
312+
})),
313+
),
314+
);
315+
316+
// Gather all the transaction in one object
317+
return transactionsAndMeta.reduce(
318+
(finalResult: DataAccessTypes.IReturnGetChannelsByTopic, channelIdAndTransactions: any) => {
319+
const id = channelIdAndTransactions.channelId;
320+
321+
// Adds the storage location of the channel's data
322+
finalResult.meta.transactionsStorageLocation[id] =
323+
channelIdAndTransactions.transactionsWithMeta.meta.transactionsStorageLocation;
324+
325+
// Adds the meta of the channel
326+
finalResult.meta.storageMeta[id] =
327+
channelIdAndTransactions.transactionsWithMeta.meta.storageMeta;
328+
329+
// Adds the transaction of the channel
330+
finalResult.result.transactions[id] =
331+
channelIdAndTransactions.transactionsWithMeta.result.transactions;
332+
333+
return finalResult;
334+
},
335+
{
336+
meta: {
337+
storageMeta: {},
338+
transactionsStorageLocation: {},
339+
},
340+
result: { transactions: {} },
341+
},
342+
);
343+
}
344+
274345
/**
275346
* Function to synchronize with the new dataIds on the storage
276347
*/

packages/data-access/src/location-by-topic.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { DataAccess as DataAccessTypes } from '@requestnetwork/types';
22
import Utils from '@requestnetwork/utils';
33

4-
// Interface of the object to store the storageLocation indexed by channel id
5-
// We use a Set data structure because dataIds are unique
6-
interface IStorageLocationByChannelId {
7-
[key: string]: Set<string>;
8-
}
9-
104
// Interface of the object to store the channel ids indexed by topic
115
interface IChannelIdByTopics {
126
[key: string]: Set<string>;
@@ -20,7 +14,7 @@ export default class LocalLocationIndex {
2014
* Storage location by channel id
2115
* maps channelId => [storageLocation]
2216
*/
23-
private storageLocationByChannelId: IStorageLocationByChannelId = {};
17+
private storageLocationByChannelId: DataAccessTypes.IStorageLocationByChannelId = {};
2418

2519
/**
2620
* Channel Ids by topic
@@ -58,11 +52,11 @@ export default class LocalLocationIndex {
5852
}
5953

6054
/**
61-
* Function to get location from a topic
55+
* Get locations from a topic
6256
*
6357
* @param topic topic to retrieve the dataId
6458
*
65-
* @return list of the location connected to the topic
59+
* @return list of the locations connected to the topic
6660
*/
6761
public getStorageLocationFromTopic(topic: string): string[] {
6862
return Utils.unique(
@@ -72,6 +66,23 @@ export default class LocalLocationIndex {
7266
).uniqueItems;
7367
}
7468

69+
/**
70+
* Get locations from a topic grouped by channel id
71+
*
72+
* @param topic topic to retrieve the storage location from
73+
*
74+
* @return list of the locations connected to the topic
75+
*/
76+
public getStorageLocationFromTopicGroupedByChannelId(topic: string): { [key: string]: string[] } {
77+
return this.getChannelIdsFromTopic(topic).reduce(
78+
(result: { [key: string]: string[] }, channelId: string) => {
79+
result[channelId] = Array.from(this.getStorageLocationsFromChannelId(channelId));
80+
return result;
81+
},
82+
{},
83+
);
84+
}
85+
7586
/**
7687
* Function to get the channel ids from a topic
7788
*

packages/data-access/test/data-access.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,59 @@ describe('data-access', () => {
402402
});
403403
});
404404

405+
describe('getChannelByTopic', () => {
406+
let dataAccess: any;
407+
408+
beforeEach(async () => {
409+
const testTopics: Promise<StorageTypes.IGetDataIdReturn> = Promise.resolve(getDataIdResult);
410+
411+
const fakeStorage: StorageTypes.IStorage = {
412+
append: chai.spy(),
413+
getData: (): any => chai.spy(),
414+
getDataId: (): any => testTopics,
415+
read: (param: string): any => {
416+
const dataIdBlock2txFake: StorageTypes.IOneContentAndMeta = {
417+
meta: { timestamp: 10 },
418+
result: { content: JSON.stringify(blockWith2tx) },
419+
};
420+
const result: any = {
421+
dataIdBlock2tx: dataIdBlock2txFake,
422+
};
423+
return result[param];
424+
},
425+
};
426+
427+
dataAccess = new DataAccess(fakeStorage);
428+
await dataAccess.initialize();
429+
});
430+
431+
it('can getChannelByTopic() with boundaries', async () => {
432+
expect(
433+
await dataAccess.getChannelsByTopic(arbitraryTopic1, { from: 9, to: 100 }),
434+
'result with arbitraryTopic1 wrong',
435+
).to.deep.equal({
436+
meta: {
437+
storageMeta: { [arbitraryId1]: [{ timestamp: 10 }] },
438+
transactionsStorageLocation: { [arbitraryId1]: ['dataIdBlock2tx'] },
439+
},
440+
result: { transactions: { [arbitraryId1]: [transactionMock1] } },
441+
});
442+
});
443+
444+
it('can getChannelByTopic() with boundaries too restrictive', async () => {
445+
expect(
446+
await dataAccess.getChannelsByTopic(arbitraryTopic1, { from: 11, to: 100 }),
447+
'result with arbitraryTopic1 wrong',
448+
).to.deep.equal({
449+
meta: {
450+
storageMeta: {},
451+
transactionsStorageLocation: {},
452+
},
453+
result: { transactions: {} },
454+
});
455+
});
456+
});
457+
405458
describe('persistTransaction', () => {
406459
it('can persistTransaction()', async () => {
407460
const fakeStorageSpied: StorageTypes.IStorage = {

packages/request-client.js/src/http-data-access.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,16 @@ export default class HttpDataAccess implements DataAccessTypes.IDataAccess {
6161
* Gets the transactions for a topic from the node through HTTP.
6262
*
6363
* @param topic The topic to search for
64+
* @param timestampBoundaries filter timestamp boundaries
6465
*/
6566
public async getTransactionsByTopic(
6667
topic: string,
68+
timestampBoundaries?: DataAccessTypes.ITimestampBoundaries,
6769
): Promise<DataAccessTypes.IReturnGetTransactions> {
6870
const { data } = await axios.get(
6971
'/getTransactionsByTopic',
7072
Object.assign(this.axiosConfig, {
71-
params: { topic },
73+
params: { topic, timestampBoundaries },
7274
}),
7375
);
7476
return data;
@@ -78,14 +80,35 @@ export default class HttpDataAccess implements DataAccessTypes.IDataAccess {
7880
* Gets the transactions for a channel from the node through HTTP.
7981
*
8082
* @param channelId The channel id to search for
83+
* @param timestampBoundaries filter timestamp boundaries
8184
*/
8285
public async getTransactionsByChannelId(
8386
channelId: string,
87+
timestampBoundaries?: DataAccessTypes.ITimestampBoundaries,
8488
): Promise<DataAccessTypes.IReturnGetTransactions> {
8589
const { data } = await axios.get(
8690
'/getTransactionsByChannelId',
8791
Object.assign(this.axiosConfig, {
88-
params: { channelId },
92+
params: { channelId, timestampBoundaries },
93+
}),
94+
);
95+
return data;
96+
}
97+
98+
/**
99+
* Gets all the transactions of channel indexed by topic from the node through HTTP.
100+
*
101+
* @param topic topic to search for
102+
* @param updatedBetween filter timestamp boundaries
103+
*/
104+
public async getChannelsByTopic(
105+
topic: string,
106+
updatedBetween?: DataAccessTypes.ITimestampBoundaries,
107+
): Promise<DataAccessTypes.IReturnGetChannelsByTopic> {
108+
const { data } = await axios.get(
109+
'/getChannelsByTopic',
110+
Object.assign(this.axiosConfig, {
111+
params: { topic, updatedBetween },
89112
}),
90113
);
91114
return data;

packages/request-client.js/test/api/request-network.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import Request from '../../src/api/request';
1010
import * as TestData from '../data-test';
1111

1212
const mockDataAccess: DataAccessTypes.IDataAccess = {
13+
async getChannelsByTopic(): Promise<any> {
14+
return;
15+
},
1316
async getTransactionsByChannelId(): Promise<any> {
1417
return;
1518
},
@@ -37,6 +40,9 @@ describe('api/request-network', () => {
3740
describe('createRequest', () => {
3841
it('cannot createRequest() with extensionsData', async () => {
3942
const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = {
43+
async getChannelsByTopic(): Promise<any> {
44+
return;
45+
},
4046
async getTransactionsByChannelId(): Promise<any> {
4147
return;
4248
},
@@ -73,6 +79,9 @@ describe('api/request-network', () => {
7379
describe('fromRequestId', () => {
7480
it('can get request with payment network fromRequestId', async () => {
7581
const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = {
82+
async getChannelsByTopic(): Promise<any> {
83+
return;
84+
},
7685
async getTransactionsByChannelId(): Promise<any> {
7786
return {
7887
result: { transactions: [{ data: JSON.stringify(TestData.action) }] },
@@ -99,6 +108,9 @@ describe('api/request-network', () => {
99108
describe('fromIdentity', () => {
100109
it('can get requests with payment network fromIdentity', async () => {
101110
const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = {
111+
async getChannelsByTopic(): Promise<any> {
112+
return;
113+
},
102114
async getTransactionsByChannelId(channelId: string): Promise<any> {
103115
let transactions: any[] = [];
104116
if (channelId === TestData.actionRequestId) {

packages/request-logic/test/index.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const fakeMetaTransactionManager = {
4343
result: { topics: [fakeTxHash] },
4444
};
4545
const fakeTransactionManager: TransactionTypes.ITransactionManager = {
46+
getChannelsByTopic: chai.spy(),
4647
getTransactionsByChannelId: chai.spy(),
4748
getTransactionsByTopic: chai.spy(),
4849
persistTransaction: chai.spy.returns(fakeMetaTransactionManager),
@@ -382,6 +383,7 @@ describe('index', () => {
382383
});
383384

384385
const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = {
386+
getChannelsByTopic: chai.spy(),
385387
getTransactionsByChannelId: (): Promise<TransactionTypes.IReturnGetTransactions> =>
386388
listActions,
387389
getTransactionsByTopic: chai.spy(),
@@ -519,6 +521,7 @@ describe('index', () => {
519521
});
520522

521523
const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = {
524+
getChannelsByTopic: chai.spy(),
522525
getTransactionsByChannelId: (): Promise<TransactionTypes.IReturnGetTransactions> =>
523526
listActions,
524527
getTransactionsByTopic: chai.spy(),
@@ -657,6 +660,7 @@ describe('index', () => {
657660
});
658661

659662
const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = {
663+
getChannelsByTopic: chai.spy(),
660664
getTransactionsByChannelId: (): Promise<TransactionTypes.IReturnGetTransactions> =>
661665
listActions,
662666
getTransactionsByTopic: chai.spy(),
@@ -739,6 +743,7 @@ describe('index', () => {
739743
});
740744

741745
const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = {
746+
getChannelsByTopic: chai.spy(),
742747
getTransactionsByChannelId: (): Promise<TransactionTypes.IReturnGetTransactions> =>
743748
listActions,
744749
getTransactionsByTopic: chai.spy(),
@@ -782,6 +787,7 @@ describe('index', () => {
782787
});
783788

784789
const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = {
790+
getChannelsByTopic: chai.spy(),
785791
getTransactionsByChannelId: (): Promise<TransactionTypes.IReturnGetTransactions> =>
786792
listActions,
787793
getTransactionsByTopic: chai.spy(),
@@ -969,6 +975,7 @@ describe('index', () => {
969975
});
970976

971977
const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = {
978+
getChannelsByTopic: chai.spy(),
972979
getTransactionsByChannelId: (
973980
topic: string,
974981
): Promise<TransactionTypes.IReturnGetTransactions> => {
@@ -1055,6 +1062,7 @@ describe('index', () => {
10551062
});
10561063

10571064
const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = {
1065+
getChannelsByTopic: chai.spy(),
10581066
getTransactionsByChannelId: (
10591067
topic: string,
10601068
): Promise<TransactionTypes.IReturnGetTransactions> => {

0 commit comments

Comments
 (0)