Skip to content

Commit e4621ab

Browse files
Merge pull request #27 from henrique021/issue25
Adding pagination support for NftRepository
2 parents 1048fad + 8fabe03 commit e4621ab

File tree

6 files changed

+130
-45
lines changed

6 files changed

+130
-45
lines changed

hedera-base/src/main/java/com/openelements/hedera/base/Nft.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
2+
13
package com.openelements.hedera.base;
24

35
import com.hedera.hashgraph.sdk.AccountId;

hedera-base/src/main/java/com/openelements/hedera/base/NftRepository.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public interface NftRepository {
2121
* @throws HederaException if the search fails
2222
*/
2323
@NonNull
24-
List<Nft> findByOwner(@NonNull AccountId ownerId) throws HederaException;
24+
Page<Nft> findByOwner(@NonNull AccountId ownerId) throws HederaException;
2525

2626
/**
2727
* Return all NFTs that are owned by the given owner.
@@ -31,7 +31,7 @@ public interface NftRepository {
3131
* @throws HederaException if the search fails
3232
*/
3333
@NonNull
34-
default List<Nft> findByOwner(@NonNull String ownerId) throws HederaException {
34+
default Page<Nft> findByOwner(@NonNull String ownerId) throws HederaException {
3535
Objects.requireNonNull(ownerId, "ownerId must not be null");
3636
return findByOwner(AccountId.fromString(ownerId));
3737
}
@@ -93,7 +93,7 @@ default Optional<Nft> findByTypeAndSerial(@NonNull String tokenId, long serialNu
9393
* @throws HederaException if the search fails
9494
*/
9595
@NonNull
96-
List<Nft> findByOwnerAndType(@NonNull AccountId ownerId, @NonNull TokenId tokenId) throws HederaException;
96+
Page<Nft> findByOwnerAndType(@NonNull AccountId ownerId, @NonNull TokenId tokenId) throws HederaException;
9797

9898
/**
9999
* Return all NFTs of a given type owned by a specific account.
@@ -104,7 +104,7 @@ default Optional<Nft> findByTypeAndSerial(@NonNull String tokenId, long serialNu
104104
* @throws HederaException if the search fails
105105
*/
106106
@NonNull
107-
default List<Nft> findByOwnerAndType(@NonNull String ownerId, @NonNull String tokenId) throws HederaException {
107+
default Page<Nft> findByOwnerAndType(@NonNull String ownerId, @NonNull String tokenId) throws HederaException {
108108
Objects.requireNonNull(ownerId, "ownerId must not be null");
109109
Objects.requireNonNull(tokenId, "tokenId must not be null");
110110
return findByOwnerAndType(AccountId.fromString(ownerId), TokenId.fromString(tokenId));

hedera-base/src/main/java/com/openelements/hedera/base/implementation/NftRepositoryImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public NftRepositoryImpl(@NonNull final MirrorNodeClient mirrorNodeClient) {
2222

2323
@NonNull
2424
@Override
25-
public List<Nft> findByOwner(@NonNull final AccountId owner) throws HederaException {
25+
public Page<Nft> findByOwner(@NonNull final AccountId owner) throws HederaException {
2626
return mirrorNodeClient.queryNftsByAccount(owner);
2727
}
2828

@@ -41,7 +41,7 @@ public Optional<Nft> findByTypeAndSerial(@NonNull final TokenId tokenId, final l
4141

4242
@NonNull
4343
@Override
44-
public List<Nft> findByOwnerAndType(@NonNull final AccountId owner, @NonNull final TokenId tokenId)
44+
public Page<Nft> findByOwnerAndType(@NonNull final AccountId owner, @NonNull final TokenId tokenId)
4545
throws HederaException {
4646
return mirrorNodeClient.queryNftsByAccountAndTokenId(owner, tokenId);
4747
}

hedera-base/src/main/java/com/openelements/hedera/base/mirrornode/MirrorNodeClient.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public interface MirrorNodeClient {
2222
* @throws HederaException if an error occurs
2323
*/
2424
@NonNull
25-
List<Nft> queryNftsByAccount(@NonNull AccountId accountId) throws HederaException;
25+
Page<Nft> queryNftsByAccount(@NonNull AccountId accountId) throws HederaException;
2626

2727
/**
2828
* Queries the NFTs owned by an account.
@@ -32,7 +32,7 @@ public interface MirrorNodeClient {
3232
* @throws HederaException if an error occurs
3333
*/
3434
@NonNull
35-
default List<Nft> queryNftsByAccount(@NonNull String accountId) throws HederaException {
35+
default Page<Nft> queryNftsByAccount(@NonNull String accountId) throws HederaException {
3636
Objects.requireNonNull(accountId, "accountId must not be null");
3737
return queryNftsByAccount(AccountId.fromString(accountId));
3838
}
@@ -46,7 +46,7 @@ default List<Nft> queryNftsByAccount(@NonNull String accountId) throws HederaExc
4646
* @throws HederaException if an error occurs
4747
*/
4848
@NonNull
49-
List<Nft> queryNftsByAccountAndTokenId(@NonNull AccountId accountId, @NonNull TokenId tokenId)
49+
Page<Nft> queryNftsByAccountAndTokenId(@NonNull AccountId accountId, @NonNull TokenId tokenId)
5050
throws HederaException;
5151

5252
/**
@@ -58,7 +58,7 @@ List<Nft> queryNftsByAccountAndTokenId(@NonNull AccountId accountId, @NonNull To
5858
* @throws HederaException if an error occurs
5959
*/
6060
@NonNull
61-
default List<Nft> queryNftsByAccountAndTokenId(@NonNull String accountId, @NonNull String tokenId)
61+
default Page<Nft> queryNftsByAccountAndTokenId(@NonNull String accountId, @NonNull String tokenId)
6262
throws HederaException {
6363
Objects.requireNonNull(accountId, "accountId must not be null");
6464
Objects.requireNonNull(tokenId, "tokenId must not be null");

hedera-spring/src/main/java/com/openelements/hedera/spring/implementation/MirrorNodeClientImpl.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,29 @@ public MirrorNodeClientImpl(final RestClient.Builder restClientBuilder) {
4747
}
4848

4949
@Override
50-
public List<Nft> queryNftsByAccount(@NonNull final AccountId accountId) throws HederaException {
50+
public Page<Nft> queryNftsByAccount(@NonNull final AccountId accountId) throws HederaException {
5151
Objects.requireNonNull(accountId, "newAccountId must not be null");
52-
final JsonNode jsonNode = doGetCall("/api/v1/accounts/" + accountId + "/nfts");
53-
return jsonNodeToNftList(jsonNode);
52+
final String path = "/api/v1/accounts/" + accountId + "/nfts";
53+
54+
final Function<JsonNode, List<Nft>> dataExtractionFunction = node -> getNfts(node);
55+
56+
57+
return new RestBasedPage<>(objectMapper, restClient.mutate().clone(), path,dataExtractionFunction);
5458
}
5559

5660
@Override
57-
public List<Nft> queryNftsByAccountAndTokenId(@NonNull final AccountId accountId, @NonNull final TokenId tokenId)
58-
throws HederaException {
59-
Objects.requireNonNull(accountId, "newAccountId must not be null");
60-
Objects.requireNonNull(tokenId, "tokenId must not be null");
61-
final JsonNode jsonNode = doGetCall("/api/v1/tokens/" + tokenId + "/nfts", Map.of("account.id", accountId));
62-
return jsonNodeToNftList(jsonNode);
63-
}
61+
public Page<Nft> queryNftsByAccountAndTokenId(@NonNull final AccountId accountId, @NonNull final TokenId tokenId)
62+
throws HederaException {
63+
Objects.requireNonNull(accountId, "accountId must not be null");
64+
Objects.requireNonNull(tokenId, "tokenId must not be null");
65+
66+
final String path = "/api/v1/tokens/" + tokenId + "/nfts/" + accountId;
67+
68+
final Function<JsonNode, List<Nft>> dataExtractionFunction = node -> getNfts(node);
69+
70+
return new RestBasedPage<>(objectMapper, restClient.mutate().clone(), path, dataExtractionFunction);
71+
72+
}
6473

6574
@Override
6675
public Page<Nft> queryNftsByTokenId(@NonNull TokenId tokenId) throws HederaException {

hedera-spring/src/test/java/com/openelements/hedera/spring/test/NftRepositoryTests.java

Lines changed: 99 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -174,29 +174,66 @@ void findByAccountId() throws Exception {
174174
hederaTestUtils.waitForMirrorNodeRecords();
175175

176176
//when
177-
final List<Nft> result = nftRepository.findByOwner(newOwner);
178-
179-
//then
180-
Assertions.assertNotNull(result);
181-
Assertions.assertEquals(2, result.size());
182-
Assertions.assertTrue(result.stream().anyMatch(nft -> nft.serial() == serial.get(0)));
183-
Assertions.assertTrue(result.stream().anyMatch(nft -> nft.serial() == serial.get(1)));
184-
}
185-
186-
@Test
187-
void findByAccountIdWIthZeroResult() throws Exception {
188-
//given
189-
final String name = "Tokemon cards";
190-
final String symbol = "TOK";
191-
final TokenId tokenId = nftClient.createNftType(name, symbol);
192-
final Account account = accountClient.createAccount();
193-
final AccountId newOwner = account.accountId();
194-
final PrivateKey newOwnerPrivateKey = account.privateKey();
195-
nftClient.associateNft(tokenId, newOwner, newOwnerPrivateKey);
196-
hederaTestUtils.waitForMirrorNodeRecords();
197-
198-
//when
199-
final List<Nft> result = nftRepository.findByOwner(newOwner);
177+
final Page<Nft> slice = nftRepository.findByOwner(newOwner);
178+
final List<Nft> result = getAll(slice);
179+
180+
// then
181+
Assertions.assertNotNull(result);
182+
Assertions.assertEquals(2, result.size());
183+
Assertions.assertTrue(result.stream().anyMatch(nft -> nft.serial() == serial.get(0)));
184+
Assertions.assertTrue(result.stream().anyMatch(nft -> nft.serial() == serial.get(1)));
185+
}
186+
187+
@Test
188+
void findByAccountIdForSomePages() throws Exception {
189+
// given
190+
final String name = "Tokemon cards";
191+
final String symbol = "TOK";
192+
193+
final AccountId adminAccountId = adminAccount.accountId();
194+
final PrivateKey adminAccountPrivateKey = adminAccount.privateKey();
195+
final Account account = accountClient.createAccount();
196+
final AccountId newOwner = account.accountId();
197+
final PrivateKey newOwnerPrivateKey = account.privateKey();
198+
final List<byte[]> metadata = IntStream.range(0, 40).mapToObj(i -> "metadata" + i)
199+
.map(s -> s.getBytes(StandardCharsets.UTF_8)).toList();
200+
final TokenId tokenId = nftClient.createNftType(name, symbol);
201+
final int batchSize = 10;
202+
for (int i = 0; i < metadata.size(); i += batchSize) {
203+
final int start = i;
204+
final int end = Math.min(i + batchSize, metadata.size());
205+
final List<Long> serial = nftClient.mintNfts(tokenId, metadata.subList(start, end).toArray(new byte[0][]));
206+
nftClient.transferNft(tokenId, serial.get(i), adminAccountId, adminAccountPrivateKey, newOwner);
207+
208+
}
209+
nftClient.associateNft(tokenId, newOwner, newOwnerPrivateKey);
210+
hederaTestUtils.waitForMirrorNodeRecords();
211+
212+
// when
213+
final Page<Nft> slice = nftRepository.findByOwner(newOwner);
214+
final List<Nft> result = getAll(slice);
215+
216+
// then
217+
Assertions.assertNotNull(result);
218+
Assertions.assertEquals(metadata.size(), result.size());
219+
220+
}
221+
222+
@Test
223+
void findByAccountIdWIthZeroResult() throws Exception {
224+
// given
225+
final String name = "Tokemon cards";
226+
final String symbol = "TOK";
227+
final TokenId tokenId = nftClient.createNftType(name, symbol);
228+
final Account account = accountClient.createAccount();
229+
final AccountId newOwner = account.accountId();
230+
final PrivateKey newOwnerPrivateKey = account.privateKey();
231+
nftClient.associateNft(tokenId, newOwner, newOwnerPrivateKey);
232+
hederaTestUtils.waitForMirrorNodeRecords();
233+
234+
// when
235+
final Page<Nft> slice = nftRepository.findByOwner(newOwner);
236+
final List<Nft> result = getAll(slice);
200237

201238
//then
202239
Assertions.assertNotNull(result);
@@ -224,14 +261,49 @@ void findByTokenIdAndAccountId() throws Exception {
224261
hederaTestUtils.waitForMirrorNodeRecords();
225262

226263
//when
227-
final List<Nft> result = nftRepository.findByOwnerAndType(newOwner, tokenId);
264+
final Page<Nft> slice = nftRepository.findByOwnerAndType(newOwner, tokenId);
265+
final List<Nft> result = getAll(slice);
228266

229267
//then
230268
Assertions.assertNotNull(result);
231269
Assertions.assertEquals(2, result.size());
232270
Assertions.assertTrue(result.stream().anyMatch(nft -> nft.serial() == serial.get(0)));
233271
Assertions.assertTrue(result.stream().anyMatch(nft -> nft.serial() == serial.get(1)));
234272
}
273+
274+
@Test
275+
void findByTokenIdAndAccountIdForSomePages() throws Exception {
276+
// given
277+
final String name = "Tokemon cards";
278+
final String symbol = "TOK";
279+
final List<byte[]> metadata = IntStream.range(0, 40).mapToObj(i -> "metadata" + i)
280+
.map(s -> s.getBytes(StandardCharsets.UTF_8)).toList();
281+
final TokenId tokenId = nftClient.createNftType(name, symbol);
282+
283+
final AccountId adminAccountId = adminAccount.accountId();
284+
final PrivateKey adminAccountPrivateKey = adminAccount.privateKey();
285+
final Account account = accountClient.createAccount();
286+
final AccountId newOwner = account.accountId();
287+
final PrivateKey newOwnerPrivateKey = account.privateKey();
288+
final int batchSize = 10;
289+
for (int i = 0; i < metadata.size(); i += batchSize) {
290+
final int start = i;
291+
final int end = Math.min(i + batchSize, metadata.size());
292+
final List<Long> serial = nftClient.mintNfts(tokenId, metadata.subList(start, end).toArray(new byte[0][]));
293+
nftClient.transferNft(tokenId, serial.get(i), adminAccountId, adminAccountPrivateKey, newOwner);
294+
295+
}
296+
nftClient.associateNft(tokenId, newOwner, newOwnerPrivateKey);
297+
hederaTestUtils.waitForMirrorNodeRecords();
298+
299+
// when
300+
final Page<Nft> slice = nftRepository.findByOwnerAndType(newOwner, tokenId);
301+
final List<Nft> result = getAll(slice);
302+
303+
// then
304+
Assertions.assertNotNull(result);
305+
Assertions.assertEquals(metadata.size(), result.size());
306+
}
235307

236308
@Test
237309
void findByTokenIdAndAccountIdWithZeroResult() throws Exception {
@@ -246,7 +318,8 @@ void findByTokenIdAndAccountIdWithZeroResult() throws Exception {
246318
hederaTestUtils.waitForMirrorNodeRecords();
247319

248320
//when
249-
final List<Nft> result = nftRepository.findByOwnerAndType(newOwner, tokenId);
321+
final Page<Nft> slice = nftRepository.findByOwnerAndType(newOwner, tokenId);
322+
final List<Nft> result = getAll(slice);
250323

251324
//then
252325
Assertions.assertNotNull(result);
@@ -335,5 +408,6 @@ void findByTokenIdAndAccountIdAndSerialWithZeroResult() throws Exception {
335408
//then
336409
Assertions.assertNotNull(result);
337410
Assertions.assertFalse(result.isPresent());
411+
338412
}
339413
}

0 commit comments

Comments
 (0)