Skip to content

Commit

Permalink
Merge pull request #61 from onflow/access-api-methods-v3
Browse files Browse the repository at this point in the history
Setting up new Access API methods
  • Loading branch information
franklywatson authored Jul 15, 2024
2 parents 323f59c + b6f17f4 commit af4b17c
Show file tree
Hide file tree
Showing 8 changed files with 524 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/AsyncFlowAccessApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ interface AsyncFlowAccessApi {
fun getNetworkParameters(): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowChainId>>

fun getLatestProtocolStateSnapshot(): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowSnapshot>>

fun getTransactionsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransaction>>>

fun getTransactionResultsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransactionResult>>>

fun getExecutionResultByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowExecutionResult?>>
}
6 changes: 6 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/FlowAccessApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,10 @@ interface FlowAccessApi {
fun getNetworkParameters(): AccessApiCallResponse<FlowChainId>

fun getLatestProtocolStateSnapshot(): AccessApiCallResponse<FlowSnapshot>

fun getTransactionsByBlockId(id: FlowId): AccessApiCallResponse<List<FlowTransaction>>

fun getTransactionResultsByBlockId(id: FlowId): AccessApiCallResponse<List<FlowTransactionResult>>

fun getExecutionResultByBlockId(id: FlowId): AccessApiCallResponse<FlowExecutionResult>
}
68 changes: 68 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/impl/AsyncFlowAccessApiImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,74 @@ class AsyncFlowAccessApiImpl(
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get latest protocol state snapshot", e))
}
}

override fun getTransactionsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransaction>>> {
return try {
completableFuture(
try {
api.getTransactionsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder().setBlockId(id.byteStringValue).build()
)
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", ex)
} else {
FlowAccessApi.AccessApiCallResponse.Success(response.transactionsList.map { FlowTransaction.of(it) })
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", e))
}
}

override fun getTransactionResultsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransactionResult>>> {
return try {
completableFuture(
try {
api.getTransactionResultsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder().setBlockId(id.byteStringValue).build()
)
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", ex)
} else {
FlowAccessApi.AccessApiCallResponse.Success(response.transactionResultsList.map { FlowTransactionResult.of(it) })
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", e))
}
}

override fun getExecutionResultByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowExecutionResult?>> {
return try {
completableFuture(
try {
api.getExecutionResultByID(Access.GetExecutionResultByIDRequest.newBuilder().setId(id.byteStringValue).build())
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", ex)
} else {
if (response.hasExecutionResult()) {
FlowAccessApi.AccessApiCallResponse.Success(FlowExecutionResult.of(response))
} else {
FlowAccessApi.AccessApiCallResponse.Error("Execution result not found")
}
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", e))
}
}
}

fun <T> completableFuture(future: ListenableFuture<T>): CompletableFuture<T> {
Expand Down
43 changes: 43 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/impl/FlowAccessApiImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,47 @@ class FlowAccessApiImpl(
FlowAccessApi.AccessApiCallResponse.Error("Failed to get latest protocol state snapshot", e)
}
}

override fun getTransactionsByBlockId(id: FlowId): FlowAccessApi.AccessApiCallResponse<List<FlowTransaction>> {
return try {
val ret = api.getTransactionsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder()
.setBlockId(id.byteStringValue)
.build()
)
FlowAccessApi.AccessApiCallResponse.Success(ret.transactionsList.map { FlowTransaction.of(it) })
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", e)
}
}

override fun getTransactionResultsByBlockId(id: FlowId): FlowAccessApi.AccessApiCallResponse<List<FlowTransactionResult>> {
return try {
val ret = api.getTransactionResultsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder()
.setBlockId(id.byteStringValue)
.build()
)
FlowAccessApi.AccessApiCallResponse.Success(ret.transactionResultsList.map { FlowTransactionResult.of(it) })
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", e)
}
}

override fun getExecutionResultByBlockId(id: FlowId): FlowAccessApi.AccessApiCallResponse<FlowExecutionResult> {
return try {
val ret = api.getExecutionResultByID(
Access.GetExecutionResultByIDRequest.newBuilder()
.setId(id.byteStringValue)
.build()
)
if (ret.hasExecutionResult()) {
FlowAccessApi.AccessApiCallResponse.Success(FlowExecutionResult.of(ret))
} else {
FlowAccessApi.AccessApiCallResponse.Error("Execution result not found")
}
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", e)
}
}
}
104 changes: 104 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,110 @@ data class FlowBlock(
}
}

data class FlowChunk(
val collectionIndex: Int,
val startState: ByteArray,
val eventCollection: ByteArray,
val blockId: FlowId,
val totalComputationUsed: Long,
val numberOfTransactions: Int,
val index: Long,
val endState: ByteArray,
val executionDataId: FlowId,
val stateDeltaCommitment: ByteArray,
) : Serializable {
companion object {
fun of(grpcExecutionResult: ExecutionResultOuterClass.Chunk) = FlowChunk(
collectionIndex = grpcExecutionResult.collectionIndex,
startState = grpcExecutionResult.startState.toByteArray(),
eventCollection = grpcExecutionResult.eventCollection.toByteArray(),
blockId = FlowId.of(grpcExecutionResult.blockId.toByteArray()),
totalComputationUsed = grpcExecutionResult.totalComputationUsed,
numberOfTransactions = grpcExecutionResult.numberOfTransactions,
index = grpcExecutionResult.index,
endState = grpcExecutionResult.endState.toByteArray(),
executionDataId = FlowId.of(grpcExecutionResult.executionDataId.toByteArray()),
stateDeltaCommitment = grpcExecutionResult.stateDeltaCommitment.toByteArray()
)
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FlowChunk) return false

if (collectionIndex != other.collectionIndex) return false
if (!startState.contentEquals(other.startState)) return false
if (!eventCollection.contentEquals(other.eventCollection)) return false
if (blockId != other.blockId) return false
if (totalComputationUsed != other.totalComputationUsed) return false
if (numberOfTransactions != other.numberOfTransactions) return false
if (index != other.index) return false
if (!endState.contentEquals(other.endState)) return false
if (executionDataId != other.executionDataId) return false
if (!stateDeltaCommitment.contentEquals(other.stateDeltaCommitment)) return false

return true
}

override fun hashCode(): Int {
var result = collectionIndex
result = 31 * result + startState.contentHashCode()
result = 31 * result + eventCollection.contentHashCode()
result = 31 * result + blockId.hashCode()
result = 31 * result + totalComputationUsed.hashCode()
result = 31 * result + numberOfTransactions
result = 31 * result + index.hashCode()
result = 31 * result + endState.contentHashCode()
result = 31 * result + executionDataId.hashCode()
result = 31 * result + stateDeltaCommitment.contentHashCode()
return result
}
}

data class FlowServiceEvent(
val type: String,
val payload: ByteArray,
) : Serializable {
companion object {
fun of(grpcExecutionResult: ExecutionResultOuterClass.ServiceEvent) = FlowServiceEvent(
type = grpcExecutionResult.type,
payload = grpcExecutionResult.payload.toByteArray(),
)
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FlowServiceEvent) return false

if (type != other.type) return false
if (!payload.contentEquals(other.payload)) return false

return true
}

override fun hashCode(): Int {
var result = type.hashCode()
result = 31 * result + payload.contentHashCode()
return result
}
}

data class FlowExecutionResult(
val blockId: FlowId,
val previousResultId: FlowId,
val chunks: List<FlowChunk>,
val serviceEvents: List<FlowServiceEvent>,
) : Serializable {
companion object {
fun of(grpcExecutionResult: Access.ExecutionResultByIDResponse) = FlowExecutionResult(
blockId = FlowId.of(grpcExecutionResult.executionResult.blockId.toByteArray()),
previousResultId = FlowId.of(grpcExecutionResult.executionResult.previousResultId.toByteArray()),
chunks = grpcExecutionResult.executionResult.chunksList.map { FlowChunk.of(it) },
serviceEvents = grpcExecutionResult.executionResult.serviceEventsList.map { FlowServiceEvent.of(it) },
)
}
}

data class FlowCollectionGuarantee(
val id: FlowId,
val signatures: List<FlowSignature>
Expand Down
80 changes: 80 additions & 0 deletions src/test/kotlin/org/onflow/flow/sdk/FlowAccessApiTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,84 @@ class FlowAccessApiTest {

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(snapshot), result)
}

@Test
fun `Test getTransactionsByBlockId`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")
val transactions = listOf(FlowTransaction.of(TransactionOuterClass.Transaction.getDefaultInstance()))
`when`(flowAccessApi.getTransactionsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactions))

val result = flowAccessApi.getTransactionsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactions), result)
}

@Test
fun `Test getTransactionsByBlockId with multiple results`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")

val transaction1 = FlowTransaction(FlowScript("script1"), emptyList(), FlowId.of("01".toByteArray()), 123L, FlowTransactionProposalKey(FlowAddress("02"), 1, 123L), FlowAddress("02"), emptyList())

val transaction2 = FlowTransaction(FlowScript("script2"), emptyList(), FlowId.of("02".toByteArray()), 456L, FlowTransactionProposalKey(FlowAddress("03"), 2, 456L), FlowAddress("03"), emptyList())

val transactions = listOf(transaction1, transaction2)

`when`(flowAccessApi.getTransactionsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactions))

val result = flowAccessApi.getTransactionsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactions), result)

assertEquals(2, transactions.size)
assertEquals(transaction1, transactions[0])
assertEquals(transaction2, transactions[1])
}

@Test
fun `Test getTransactionResultsByBlockId`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")
val transactionResults = listOf(FlowTransactionResult.of(Access.TransactionResultResponse.getDefaultInstance()))
`when`(flowAccessApi.getTransactionResultsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactionResults))

val result = flowAccessApi.getTransactionResultsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactionResults), result)
}

@Test
fun `Test getTransactionResultsByBlockId with multiple results`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")

val transactionResult1 = FlowTransactionResult(FlowTransactionStatus.SEALED, 1, "message1", emptyList())

val transactionResult2 = FlowTransactionResult(FlowTransactionStatus.SEALED, 2, "message2", emptyList())

val transactions = listOf(transactionResult1, transactionResult2)

`when`(flowAccessApi.getTransactionResultsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactions))

val result = flowAccessApi.getTransactionResultsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactions), result)

assertEquals(2, FlowAccessApi.AccessApiCallResponse.Success(transactions).data.size)
assertEquals(transactionResult1, FlowAccessApi.AccessApiCallResponse.Success(transactions).data[0])
assertEquals(transactionResult2, FlowAccessApi.AccessApiCallResponse.Success(transactions).data[1])
}

@Test
fun `Test getExecutionResultByBlockId`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")
val executionResult = FlowExecutionResult.of(Access.ExecutionResultByIDResponse.getDefaultInstance())
`when`(flowAccessApi.getExecutionResultByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(executionResult))

val result = flowAccessApi.getExecutionResultByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(executionResult), result)
}
}
Loading

0 comments on commit af4b17c

Please sign in to comment.