Skip to content

Commit 262a60d

Browse files
Merge pull request #61 from belloibrahv/issue-39-create-transaction-repository
feat: implement TransactionRepository for Hedera Mirror Node integration
2 parents 4038d0b + 90fa2b5 commit 262a60d

File tree

5 files changed

+152
-23
lines changed

5 files changed

+152
-23
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.openelements.hedera.base;
2+
3+
import java.util.Objects;
4+
import java.util.Optional;
5+
6+
import org.jspecify.annotations.NonNull;
7+
8+
import com.hedera.hashgraph.sdk.AccountId;
9+
import com.openelements.hedera.base.mirrornode.Page;
10+
import com.openelements.hedera.base.mirrornode.TransactionInfo;
11+
12+
/**
13+
* Interface for interacting with transactions on a Hedera network.
14+
* This interface provides methods for querying transactions.
15+
*/
16+
public interface TransactionRepository {
17+
/**
18+
* Find all transactions associated with a specific account.
19+
*
20+
* @param accountId id of the account
21+
* @return page of transactions
22+
* @throws HederaException if the search fails
23+
*/
24+
@NonNull
25+
Page<TransactionInfo> findByAccount(@NonNull AccountId accountId) throws HederaException;
26+
27+
/**
28+
* Find all transactions associated with a specific account.
29+
*
30+
* @param accountId id of the account as a string
31+
* @return page of transactions
32+
* @throws HederaException if the search fails
33+
*/
34+
@NonNull
35+
default Page<TransactionInfo> findByAccount(@NonNull String accountId) throws HederaException {
36+
Objects.requireNonNull(accountId, "accountId must not be null");
37+
return findByAccount(AccountId.fromString(accountId));
38+
}
39+
40+
/**
41+
* Find a specific transaction by its ID.
42+
*
43+
* @param transactionId the transaction ID
44+
* @return Optional containing the transaction if found
45+
* @throws HederaException if the search fails
46+
*/
47+
@NonNull
48+
Optional<TransactionInfo> findById(@NonNull String transactionId) throws HederaException;
49+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.openelements.hedera.base.implementation;
2+
3+
import java.util.Objects;
4+
import java.util.Optional;
5+
6+
import org.jspecify.annotations.NonNull;
7+
8+
import com.hedera.hashgraph.sdk.AccountId;
9+
import com.openelements.hedera.base.HederaException;
10+
import com.openelements.hedera.base.TransactionRepository;
11+
import com.openelements.hedera.base.mirrornode.MirrorNodeClient;
12+
import com.openelements.hedera.base.mirrornode.Page;
13+
import com.openelements.hedera.base.mirrornode.TransactionInfo;
14+
15+
16+
public class TransactionRepositoryImpl implements TransactionRepository {
17+
private final MirrorNodeClient mirrorNodeClient;
18+
19+
public TransactionRepositoryImpl(@NonNull final MirrorNodeClient mirrorNodeClient) {
20+
this.mirrorNodeClient = Objects.requireNonNull(mirrorNodeClient, "mirrorNodeClient must not be null");
21+
}
22+
23+
@NonNull
24+
@Override
25+
public Page<TransactionInfo> findByAccount(@NonNull final AccountId accountId) throws HederaException {
26+
Objects.requireNonNull(accountId, "accountId must not be null");
27+
return this.mirrorNodeClient.queryTransactionsByAccount(accountId);
28+
}
29+
30+
@NonNull
31+
@Override
32+
public Optional<TransactionInfo> findById(@NonNull final String transactionId) throws HederaException {
33+
Objects.requireNonNull(transactionId, "transactionId must not be null");
34+
return this.mirrorNodeClient.queryTransaction(transactionId);
35+
}
36+
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package com.openelements.hedera.base.mirrornode;
22

3+
import java.util.Objects;
4+
import java.util.Optional;
5+
6+
import org.jspecify.annotations.NonNull;
7+
38
import com.hedera.hashgraph.sdk.AccountId;
49
import com.hedera.hashgraph.sdk.TokenId;
510
import com.openelements.hedera.base.HederaException;
611
import com.openelements.hedera.base.Nft;
7-
import java.util.Objects;
8-
import java.util.Optional;
9-
import org.jspecify.annotations.NonNull;
1012

1113
/**
1214
* A client for querying the Hedera Mirror Node REST API.
@@ -144,6 +146,17 @@ default Optional<Nft> queryNftsByAccountAndTokenIdAndSerial(@NonNull String acco
144146
serialNumber);
145147
}
146148

149+
/**
150+
* Queries all transactions for a specific account.
151+
*
152+
* @param accountId the account ID to query transactions for
153+
* @return a page of transaction information
154+
* @throws HederaException if an error occurs during the query
155+
*/
156+
@NonNull
157+
Page<TransactionInfo> queryTransactionsByAccount(@NonNull AccountId accountId) throws HederaException;
158+
159+
147160
/**
148161
* Queries the transaction information for a specific transaction ID.
149162
*

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

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
package com.openelements.hedera.spring.implementation;
22

3-
import com.fasterxml.jackson.core.JsonProcessingException;
4-
import com.fasterxml.jackson.databind.JsonNode;
5-
import com.fasterxml.jackson.databind.ObjectMapper;
6-
import com.hedera.hashgraph.sdk.AccountId;
7-
import com.hedera.hashgraph.sdk.TokenId;
8-
import com.openelements.hedera.base.HederaException;
9-
import com.openelements.hedera.base.Nft;
10-
import com.openelements.hedera.base.mirrornode.AccountInfo;
11-
import com.openelements.hedera.base.mirrornode.MirrorNodeClient;
12-
import com.openelements.hedera.base.mirrornode.Page;
13-
import com.openelements.hedera.base.mirrornode.TransactionInfo;
143
import java.io.IOException;
154
import java.net.URI;
165
import java.util.List;
@@ -21,6 +10,7 @@
2110
import java.util.Spliterators;
2211
import java.util.function.Function;
2312
import java.util.stream.StreamSupport;
13+
2414
import org.jspecify.annotations.NonNull;
2515
import org.springframework.http.HttpStatus;
2616
import org.springframework.http.HttpStatusCode;
@@ -29,6 +19,18 @@
2919
import org.springframework.web.client.RestClient;
3020
import org.springframework.web.util.UriBuilder;
3121

22+
import com.fasterxml.jackson.core.JsonProcessingException;
23+
import com.fasterxml.jackson.databind.JsonNode;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import com.hedera.hashgraph.sdk.AccountId;
26+
import com.hedera.hashgraph.sdk.TokenId;
27+
import com.openelements.hedera.base.HederaException;
28+
import com.openelements.hedera.base.Nft;
29+
import com.openelements.hedera.base.mirrornode.AccountInfo;
30+
import com.openelements.hedera.base.mirrornode.MirrorNodeClient;
31+
import com.openelements.hedera.base.mirrornode.Page;
32+
import com.openelements.hedera.base.mirrornode.TransactionInfo;
33+
3234
public class MirrorNodeClientImpl implements MirrorNodeClient {
3335

3436
private final ObjectMapper objectMapper;
@@ -89,6 +91,14 @@ public Optional<Nft> queryNftsByAccountAndTokenIdAndSerial(@NonNull final Accoun
8991
.filter(nft -> Objects.equals(nft.owner(), accountId));
9092
}
9193

94+
@Override
95+
public Page<TransactionInfo> queryTransactionsByAccount(@NonNull final AccountId accountId) throws HederaException {
96+
Objects.requireNonNull(accountId, "accountId must not be null");
97+
final String path = "/api/v1/transactions?account.id=" + accountId.toString();
98+
final Function<JsonNode, List<TransactionInfo>> dataExtractionFunction = this::extractTransactionInfoFromJsonNode;
99+
return new RestBasedPage<>(objectMapper, restClient.mutate().clone(), path, dataExtractionFunction);
100+
}
101+
92102
@Override
93103
public Optional<TransactionInfo> queryTransaction(@NonNull final String transactionId) throws HederaException {
94104
Objects.requireNonNull(transactionId, "transactionId must not be null");
@@ -222,4 +232,23 @@ private List<Nft> getNfts(final JsonNode jsonNode) {
222232
}
223233
}).toList();
224234
}
235+
236+
private List<TransactionInfo> extractTransactionInfoFromJsonNode(JsonNode jsonNode) {
237+
if (!jsonNode.has("transactions")) {
238+
return List.of();
239+
}
240+
final JsonNode transactionsNode = jsonNode.get("transactions");
241+
if (!transactionsNode.isArray()) {
242+
throw new IllegalArgumentException("Transactions node is not an array: " + transactionsNode);
243+
}
244+
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(transactionsNode.iterator(), Spliterator.ORDERED), false)
245+
.map(transactionNode -> {
246+
try {
247+
final String transactionId = transactionNode.get("transaction_id").asText();
248+
return new TransactionInfo(transactionId);
249+
} catch (final Exception e) {
250+
throw new RuntimeException("Error parsing transaction from JSON '" + transactionNode + "'", e);
251+
}
252+
}).toList();
253+
}
225254
}

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
package com.openelements.hedera.spring.test;
22

3-
import com.hedera.hashgraph.sdk.AccountId;
4-
import com.hedera.hashgraph.sdk.PrivateKey;
5-
import com.hedera.hashgraph.sdk.TokenId;
6-
import com.openelements.hedera.base.Account;
7-
import com.openelements.hedera.base.AccountClient;
8-
import com.openelements.hedera.base.Nft;
9-
import com.openelements.hedera.base.NftClient;
10-
import com.openelements.hedera.base.NftRepository;
11-
import com.openelements.hedera.base.mirrornode.Page;
123
import java.nio.charset.StandardCharsets;
134
import java.util.ArrayList;
145
import java.util.List;
156
import java.util.Optional;
167
import java.util.stream.IntStream;
8+
179
import org.junit.jupiter.api.Assertions;
1810
import org.junit.jupiter.api.Test;
1911
import org.springframework.beans.factory.annotation.Autowired;
2012
import org.springframework.boot.test.context.SpringBootTest;
2113

14+
import com.hedera.hashgraph.sdk.AccountId;
15+
import com.hedera.hashgraph.sdk.PrivateKey;
16+
import com.hedera.hashgraph.sdk.TokenId;
17+
import com.openelements.hedera.base.Account;
18+
import com.openelements.hedera.base.AccountClient;
19+
import com.openelements.hedera.base.Nft;
20+
import com.openelements.hedera.base.NftClient;
21+
import com.openelements.hedera.base.NftRepository;
22+
import com.openelements.hedera.base.mirrornode.Page;
23+
2224
@SpringBootTest(classes = TestConfig.class)
2325
public class NftRepositoryTests {
2426

0 commit comments

Comments
 (0)