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

Address code duplication in Access API tests and error handling #140

Merged
merged 25 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions java-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Below is a list of all Java code examples currently supported in this repo:

[Get collections by ID.](src/main/java/org/onflow/examples/java/getCollection/GetCollectionAccessAPIConnector.java)

- Get collection by id
- Get full collection by id (returns all transactions in collection response)

#### Get Execution Data

[Get execution data by block ID.](src/main/java/org/onflow/examples/java/getExecutionData/GetExecutionDataAccessAPIConnector.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import org.onflow.flow.sdk.FlowAccessApi.AccessApiCallResponse;
import org.onflow.flow.sdk.FlowCollection;
import org.onflow.flow.sdk.FlowId;
import org.onflow.flow.sdk.FlowTransaction;

import java.util.List;

public class GetCollectionAccessAPIConnector {
private final FlowAccessApi accessAPI;
Expand All @@ -21,4 +24,14 @@ public FlowCollection getCollectionById(FlowId collectionId) {
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public List<FlowTransaction> getFullCollectionById(FlowId collectionId) {
AccessApiCallResponse<List<FlowTransaction>> response = accessAPI.getFullCollectionById(collectionId);
if (response instanceof AccessApiCallResponse.Success) {
return ((AccessApiCallResponse.Success<List<FlowTransaction>>) response).getData();
} else {
AccessApiCallResponse.Error errorResponse = (AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
franklywatson marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.onflow.examples.java.getProtocolState;

import org.onflow.flow.sdk.FlowAccessApi;
import org.onflow.flow.sdk.FlowId;
import org.onflow.flow.sdk.FlowSnapshot;

public class GetProtocolStateAccessAPIConnector {
private final FlowAccessApi accessAPI;

public GetProtocolStateAccessAPIConnector(FlowAccessApi accessAPI) {
this.accessAPI = accessAPI;
}

public FlowSnapshot getLatestProtocolStateSnapshot() {
FlowAccessApi.AccessApiCallResponse<FlowSnapshot> response = accessAPI.getLatestProtocolStateSnapshot();
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowSnapshot>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public FlowSnapshot getProtocolStateSnapshotByBlockId(FlowId blockId) {
FlowAccessApi.AccessApiCallResponse<FlowSnapshot> response = accessAPI.getProtocolStateSnapshotByBlockId(blockId);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowSnapshot>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public FlowSnapshot getProtocolStateSnapshotByHeight(Long height) {
FlowAccessApi.AccessApiCallResponse<FlowSnapshot> response = accessAPI.getProtocolStateSnapshotByHeight(height);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowSnapshot>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
franklywatson marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import org.onflow.flow.sdk.crypto.Crypto;
import org.onflow.flow.sdk.crypto.PublicKey;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
Expand Down Expand Up @@ -51,4 +53,16 @@ public void canFetchCollectionById() {
assertNotNull(collection, "Collection should not be null");
assertEquals(collectionId, collection.getId(), "Collection ID should match the fetched collection ID");
}

@Test
public void canFetchFullCollectionById() {
List<FlowTransaction> fullCollectionResponse = connector.getFullCollectionById(collectionId);

assertNotNull(fullCollectionResponse, "Collection transactions should not be null");
assertFalse(fullCollectionResponse.isEmpty(), "Collection transactions should not be empty");

FlowTransaction firstTransaction = fullCollectionResponse.get(0);
assertNotNull(firstTransaction.getId(), "Transaction ID should not be null");
assertNotNull(firstTransaction.getScript(), "Transaction script should not be null");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.onflow.examples.java.getProtocolState;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.onflow.flow.common.test.FlowEmulatorProjectTest;
import org.onflow.flow.common.test.FlowTestClient;
import org.onflow.flow.sdk.FlowAccessApi;
import org.onflow.flow.sdk.FlowBlock;
import org.onflow.flow.sdk.FlowSnapshot;

import static org.junit.jupiter.api.Assertions.*;

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
public class GetProtocolStateAccessAPIConnectorTest {
@FlowTestClient
private FlowAccessApi accessAPI;
private FlowBlock block;

private GetProtocolStateAccessAPIConnector protocolStateConnector;

@BeforeEach
public void setup() {
protocolStateConnector = new GetProtocolStateAccessAPIConnector(accessAPI);
}

@Test
public void canGetLatestProtocolStateSnapshot() {
FlowSnapshot latestSnapshot = protocolStateConnector.getLatestProtocolStateSnapshot();
assertNotNull(latestSnapshot, "Latest snapshot should not be null");
}

@Test
public void canGetProtocolStateSnapshotByBlockId() {
block = getLatestBlock();
FlowSnapshot snapshot = protocolStateConnector.getProtocolStateSnapshotByBlockId(block.getId());
assertNotNull(snapshot, "Snapshot should not be null");
}

@Test
public void canGetProtocolStateSnapshotByHeight() {
block = getLatestBlock();
FlowSnapshot snapshot = protocolStateConnector.getProtocolStateSnapshotByHeight(block.getHeight());
assertNotNull(snapshot, "Snapshot should not be null");
}
franklywatson marked this conversation as resolved.
Show resolved Hide resolved

private FlowBlock getLatestBlock() {
FlowAccessApi.AccessApiCallResponse<FlowBlock> response = accessAPI.getLatestBlock(true, false);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
Comment on lines +46 to +54
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor error handling to reduce duplication

Given the PR's objective to address error handling duplication, consider extracting the response handling pattern into a shared utility method. This would align with the goal of reducing code duplication across the Access API.

+private <T> T handleApiResponse(FlowAccessApi.AccessApiCallResponse<T> response) {
+    if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
+        return ((FlowAccessApi.AccessApiCallResponse.Success<T>) response).getData();
+    }
+    FlowAccessApi.AccessApiCallResponse.Error errorResponse = 
+        (FlowAccessApi.AccessApiCallResponse.Error) response;
+    throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
+}

 private FlowBlock getLatestBlock() {
-    FlowAccessApi.AccessApiCallResponse<FlowBlock> response = accessAPI.getLatestBlock(true, false);
-    if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
-        return ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) response).getData();
-    } else {
-        FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
-        throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
-    }
+    return handleApiResponse(accessAPI.getLatestBlock(true, false));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private FlowBlock getLatestBlock() {
FlowAccessApi.AccessApiCallResponse<FlowBlock> response = accessAPI.getLatestBlock(true, false);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
private <T> T handleApiResponse(FlowAccessApi.AccessApiCallResponse<T> response) {
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<T>) response).getData();
}
FlowAccessApi.AccessApiCallResponse.Error errorResponse =
(FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
private FlowBlock getLatestBlock() {
return handleApiResponse(accessAPI.getLatestBlock(true, false));
}

}
3 changes: 3 additions & 0 deletions kotlin-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ Below is a list of all Kotlin code examples currently supported in this repo:

[Get collections by ID.](src/main/kotlin/org/onflow/examples/kotlin/getCollection/GetCollectionAccessAPIConnector.kt)

- Get collection by id
- Get full collection by id (returns all transactions in collection response)

#### Get Execution Data

[Get execution data by block ID.](src/main/kotlin/org/onflow/examples/kotlin/getExecutionData/GetExecutionDataAccessAPIConnector.kt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,10 @@ class GetCollectionAccessAPIConnector(
is AccessApiCallResponse.Success -> response.data
is AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getFullCollectionById(collectionId: FlowId): List<FlowTransaction> =
when (val response = accessAPI.getFullCollectionById(collectionId)) {
is AccessApiCallResponse.Success -> response.data
is AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
franklywatson marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.onflow.examples.kotlin.getProtocolState

import org.onflow.flow.sdk.FlowAccessApi
import org.onflow.flow.sdk.FlowId
import org.onflow.flow.sdk.FlowSnapshot

internal class GetProtocolStateAccessAPIConnector(
private val accessAPI: FlowAccessApi
) {
fun getLatestProtocolStateSnapshot(): FlowSnapshot =
when (val response = accessAPI.getLatestProtocolStateSnapshot()) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getProtocolStateSnapshotByBlockId(blockId: FlowId): FlowSnapshot =
when (val response = accessAPI.getProtocolStateSnapshotByBlockId(blockId)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getProtocolStateSnapshotByHeight(height: Long): FlowSnapshot =
when (val response = accessAPI.getProtocolStateSnapshotByHeight(height)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
}
franklywatson marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import org.onflow.flow.common.test.FlowEmulatorProjectTest
import org.onflow.flow.common.test.FlowServiceAccountCredentials
import org.onflow.flow.common.test.FlowTestClient
import org.onflow.flow.common.test.TestAccount
import org.onflow.flow.sdk.FlowAccessApi
import org.onflow.flow.sdk.FlowCollection
import org.onflow.flow.sdk.FlowId
import org.onflow.flow.sdk.SignatureAlgorithm
import org.onflow.flow.sdk.*
import org.onflow.flow.sdk.crypto.Crypto

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
Expand Down Expand Up @@ -53,4 +50,16 @@ class GetCollectionAccessAPIConnectorTest {
assertNotNull(collection, "Collection should not be null")
assertEquals(collectionId, collection.id, "Collection ID should match the fetched collection ID")
}

@Test
fun `Can fetch full collection by ID`() {
val collectionTransactions: List<FlowTransaction> = connector.getFullCollectionById(collectionId)

assertNotNull(collectionTransactions, "Collection transactions should not be null")
assertTrue(collectionTransactions.isNotEmpty(), "Collection transactions should not be empty")

val firstTransaction = collectionTransactions.first()
assertNotNull(firstTransaction.id, "Transaction ID should not be null")
assertNotNull(firstTransaction.script, "Transaction script should not be null")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.onflow.flow.sdk.FlowAccessApi
import org.onflow.flow.sdk.FlowChainId

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
internal class GetNetworkParametersAccessAPIConnectorTest {
internal class GetNetworkParamsAccessAPIConnectorTest {
@FlowTestClient
lateinit var accessAPI: FlowAccessApi

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.onflow.examples.kotlin.getProtocolState

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.onflow.flow.common.test.FlowEmulatorProjectTest
import org.onflow.flow.common.test.FlowTestClient
import org.onflow.flow.sdk.FlowAccessApi
import org.onflow.flow.sdk.FlowBlock
import org.onflow.flow.sdk.FlowSnapshot

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
internal class GetProtocolStateAccessAPIConnectorTest {
@FlowTestClient
lateinit var accessAPI: FlowAccessApi
lateinit var block: FlowBlock

private lateinit var protocolStateConnector: GetProtocolStateAccessAPIConnector

@BeforeEach
fun setup() {
protocolStateConnector = GetProtocolStateAccessAPIConnector(accessAPI)
}

@Test
fun `Can get latest protocol state snapshot`() {
val latestSnapshot: FlowSnapshot = protocolStateConnector.getLatestProtocolStateSnapshot()
assertNotNull(latestSnapshot, "Latest snapshot should not be null")
}

@Test
fun `Can get protocol state snapshot by blockId`() {
block = when (val response = accessAPI.getLatestBlock()) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
franklywatson marked this conversation as resolved.
Show resolved Hide resolved

val latestSnapshot: FlowSnapshot = protocolStateConnector.getProtocolStateSnapshotByBlockId(block.id)
assertNotNull(latestSnapshot, ("Snapshot should not be null"))
}

@Test
fun `Can get protocol state snapshot by height`() {
block = when (val response = accessAPI.getLatestBlock()) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

val latestSnapshot: FlowSnapshot = protocolStateConnector.getProtocolStateSnapshotByHeight(block.height)
assertNotNull(latestSnapshot, ("Snapshot should not be null"))
}
}
Loading
Loading