Skip to content

Fix MicroProfile transaction queries: wrong endpoint, broken serialization, and JSON parsing errors#56

Open
alejandroGM0 wants to merge 10 commits intohiero-ledger:mainfrom
alejandroGM0:fix/microprofile-transaction-queries
Open

Fix MicroProfile transaction queries: wrong endpoint, broken serialization, and JSON parsing errors#56
alejandroGM0 wants to merge 10 commits intohiero-ledger:mainfrom
alejandroGM0:fix/microprofile-transaction-queries

Conversation

@alejandroGM0
Copy link
Copy Markdown
Contributor

Fixes #55

Summary

Fixes all four MirrorNodeClientImpl transaction query methods that were hitting /api/v1/tokens instead of /api/v1/transactions, making them return token listings instead of transaction data. Also fixes enum serialization so the mirror-node receives the correct parameter values.

Beyond the endpoint fix, the JSON response parser (MirrorNodeJsonConverterImpl) crashes on live mirror-node transaction responses due to type mismatches and null handling errors. These are fixed together since the parser bugs are on the same code path and would be the immediate next failure after correcting the endpoint.

Changes

MirrorNodeClientImpl.java (MicroProfile)

  • Extract TRANSACTIONS_PATH, TOKENS_PATH, TOPICS_PATH constants
  • Fix endpoint: /api/v1/tokens -> /api/v1/transactions in all 4 transaction query methods
  • Fix queryTransactionsByAccountAndType: type -> type.getType() (sends protocol string CRYPTOCREATEACCOUNT instead of Java enum ACCOUNT_CREATE)
  • Fix queryTransactionsByAccountAndResult: result -> result.name()
  • Fix queryTransactionsByAccountAndModification: type -> type.name()

MirrorNodeJsonConverterImpl.java (MicroProfile)

  • Fix charged_tx_fee, amount, serial_number parsing: Long.parseLong(getString(...)) -> getJsonNumber(...).longValue() (mirror-node returns JSON numbers)
  • Fix bytes, entity_id, node parsing: getString() -> getNullableString() helper (mirror-node returns JSON null for these fields)
  • Fix parent_consensus_timestamp null check: .get(...).asJsonObject() == null -> jsonObject.isNull(key)
  • Fix jsonArrayToStream: remove throw on empty array (empty result sets are valid)

MirrorNodeClientImpl.java (Spring)

  • Extract ACCOUNTS_PATH, TRANSACTIONS_PATH, TOKENS_PATH, TOPICS_PATH constants (consistency, no bug fix)

New tests

  • MirrorNodeClientImplPathTest — 5 unit tests verifying path construction (no live network needed)
  • TransactionRepositoryTest — 5 integration tests matching the existing Spring TransactionRepositoryTest

How verified

  • Unit tests pass: mvn test -pl hiero-enterprise-microprofile -Dtest=MirrorNodeClientImplPathTest (5/5)
  • Confirmed via git show HEAD:...MirrorNodeClientImpl.java that the original code uses /api/v1/tokens
  • Confirmed via live Hedera mainnet mirror-node that /api/v1/tokens?account.id=0.0.98 returns token data, while /api/v1/transactions?account.id=0.0.98 returns transaction data
  • Confirmed mirror-node rejects transactiontype=ACCOUNT_CREATE but accepts transactiontype=CRYPTOCREATEACCOUNT

Copy link
Copy Markdown
Member

@Ndacyayisenga-droid Ndacyayisenga-droid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alejandroGM0. PR looks good.

Could you fix the formatting issues with mvn spotless:apply

And for some reason I can't be able to merge your Prs

…ation, and JSON parsing errors

Fix all four MirrorNodeClientImpl transaction query methods that were hitting
/api/v1/tokens instead of /api/v1/transactions, returning token listings
instead of transaction data. Also fix enum serialization so the mirror-node
receives correct parameter values.

MirrorNodeClientImpl (MicroProfile):
- Extract TRANSACTIONS_PATH, TOKENS_PATH, TOPICS_PATH constants
- Fix endpoint: /api/v1/tokens -> /api/v1/transactions in all 4 methods
- Fix queryTransactionsByAccountAndType: type -> type.getType()
- Fix queryTransactionsByAccountAndResult: result -> result.name()
- Fix queryTransactionsByAccountAndModification: type -> type.name()

MirrorNodeJsonConverterImpl (MicroProfile):
- Fix charged_tx_fee, amount, serial_number: getString+parseLong -> getJsonNumber
- Fix bytes, entity_id, node: getString -> getNullableString helper
- Fix parent_consensus_timestamp null check: use jsonObject.isNull()
- Fix jsonArrayToStream: remove throw on empty arrays

MirrorNodeClientImpl (Spring):
- Extract ACCOUNTS_PATH, TRANSACTIONS_PATH, TOKENS_PATH, TOPICS_PATH constants

New tests:
- MirrorNodeClientImplPathTest: 5 unit tests verifying URL path construction
- TransactionRepositoryTest: 5 integration tests matching Spring parity

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
`queryTransactionsByAccountAndType` had a 6-space continuation indent
that failed `spotless:check` in CI. Bring it to 8 spaces to match the
project format.

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
Run `mvn spotless:apply` over the files touched in this PR.

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
@alejandroGM0 alejandroGM0 force-pushed the fix/microprofile-transaction-queries branch from 4088bd7 to 3baa7aa Compare April 19, 2026 10:00
@alejandroGM0
Copy link
Copy Markdown
Contributor Author

Thanks @alejandroGM0. PR looks good.

Could you fix the formatting issues with mvn spotless:apply

And for some reason I can't be able to merge your Prs

Done
I've also fixed the issue with pull requests (it was a git sign problem).

HieroTestProducer is @ApplicationScoped and discovered automatically by CDI.

Adding @AddBean(HieroTestProducer.class) registered it a second time, causing

WELD-001409 Ambiguous dependencies for HieroTestUtils in TransactionRepositoryTest.

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
Previous run hit a flaky mirror-node indexing race in TransactionRepositoryTest.testFindTransactionByAccountId.

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
@alejandroGM0
Copy link
Copy Markdown
Contributor Author

There seems to be a race condition in the tests causing the flow tests to fail randomly (not always): the test creates a new account and immediately asks the mirror node to list its transactions, but the "by account" index sometimes takes a few hundred ms longer to update than the "by tx-id" one, so the list comes back empty. I'm looking into this.

HieroTestUtils.waitForMirrorNodeRecords() only polls the by-transaction-id

index, but tests that assert on results obtained through the by-account

index can still observe an empty page for a freshly created account because

both indexes are updated independently.

Add an overload that, after the existing wait, also polls

queryTransactionsByAccount(accountId) until it returns data. Use it in the

Spring and MicroProfile tests that assert assertFalse(data.isEmpty()).

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
@alejandroGM0
Copy link
Copy Markdown
Contributor Author

alejandroGM0 commented Apr 20, 2026

There seems to be a race condition in the tests causing the flow tests to fail randomly (not always): the test creates a new account and immediately asks the mirror node to list its transactions, but the "by account" index sometimes takes a few hundred ms longer to update than the "by tx-id" one, so the list comes back empty. I'm looking into this.

@Ndacyayisenga-droid
The last commit should fix a pre-existing race in HieroTestUtils.waitForMirrorNodeRecords(): it only polled the tx-id index, while findByAccount hits a separate account-id index that can lag. Added a waitForMirrorNodeRecords(AccountId) overload and used it in the Spring and MicroProfile testFindTransactionByAccountId

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not part of your changes but throwing UnsupportedOperationException for now here makes more sense

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

* A test subclass that captures the path passed to RestBasedPage without making any HTTP call.
* This lets us verify the URL path construction in isolation.
*/
static class PathCapturingMirrorNodeClient extends MirrorNodeClientImpl {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is never referenced anywhere, and the comment describes capturing the path without HTTP, but nothing in the suite does that yet. That makes the class look like a leftover spike. I would suggest we remove if we dont use it anywhere

}

@Test
void testFindTransactionByAccountIdGiveEmptyListForAccountIdWithZeroTransaction()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method too long I would rename it to something like findByAccountReturnsEmptyWhenNoTransactions()

final Page<TransactionInfo> page =
transactionRepository.findByAccountAndType(
account.accountId(), TransactionType.ACCOUNT_CREATE);
Assertions.assertNotNull(page);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertNotNull(page) does not prove the repository or mirror client applied the filter, an empty page or wrong rows would still pass. Consider asserting page.getData() is non-empty when you expect activity, and that every returned TransactionInfo matches the requested type / result / modification (or assert on fields you trust from the mirror response).

hieroTestUtils.waitForMirrorNodeRecords();
final Page<TransactionInfo> page =
transactionRepository.findByAccountAndResult(account.accountId(), Result.SUCCESS);
Assertions.assertNotNull(page);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here!

hieroTestUtils.waitForMirrorNodeRecords();
final Page<TransactionInfo> page =
transactionRepository.findByAccountAndModification(
account.accountId(), BalanceModification.DEBIT);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would instead use CREDIT as DEBIT often yields no rows for a brand-new id!

final Page<TransactionInfo> page =
transactionRepository.findByAccountAndModification(
account.accountId(), BalanceModification.DEBIT);
Assertions.assertNotNull(page);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

final Account account = accountClient.createAccount(1);
hieroTestUtils.waitForMirrorNodeRecords(account.accountId());
final Page<TransactionInfo> page = transactionRepository.findByAccount(account.accountId());
Assertions.assertNotNull(page);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If transactionRepository.findByAccount(...) guarantees a non-null Page, then assertNotNull(page) is redundant since page.getData() would already fail if null. Fine to keep for readability, otherwise you could rely on the getData() assertions alone.

Keep the PR focused on the maintainer feedback by tightening the MirrorNode query tests, centralizing null-check messages, and redacting test config values during local runs.

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
Keep the current PR focused on the maintainer-requested transaction query changes.
The TestConfigSource logging change is being handled separately.

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
@alejandroGM0 alejandroGM0 force-pushed the fix/microprofile-transaction-queries branch from 6511ce9 to 55cb10b Compare April 23, 2026 15:28
Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
Keep the null-check message extraction aligned with the repository formatting rules so the MicroProfile Spotless check passes in CI.

Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MicroProfile transaction queries hit wrong endpoint and crash on live mirror-node responses

2 participants