Skip to content

Commit

Permalink
adding custom stateJSON and query sample
Browse files Browse the repository at this point in the history
  • Loading branch information
peterli-r3 committed May 13, 2024
1 parent 2fa0b42 commit a1c3b94
Show file tree
Hide file tree
Showing 63 changed files with 147 additions and 2,011 deletions.
112 changes: 112 additions & 0 deletions kotlin-samples/customStateJSONandQuery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Custom StateJSON and Query Cordapp -kotlin (Corda v5.2)

## This sample repository provides:

A sample CorDapp that demonstrate the functionalities of storing custom stateJSON into the vNode's database and the functionalities of querying
specific states via the custom stateJSON. The functions are demo on the Chat app that are in our template app.
We will be seeing both Alice and Dave sending a chat entry to Bob, and we will query only Alice's chat entry via the custom
query.

## Prerequisite
1. Java 17
2. Corda-cli (v5.2), Download [here](https://github.com/corda/corda-runtime-os/releases/tag/release-5.2.0.0). You need to install Java 17 first.
3. Docker Desktop

## Setting up

1. We will begin our test deployment with clicking the `startCorda`. This task will load up the combined Corda workers in docker.
A successful deployment will allow you to open the REST APIs at: https://localhost:8888/api/v5_2/swagger#. You can test out some of the
functions to check connectivity. (GET /cpi function call should return an empty list as for now.)
2. We will now deploy the cordapp with a click of `vNodeSetup` task. Upon successful deployment of the CPI, the GET /cpi function call should now return the meta data of the cpi you just upload

## Flow Management Tool[Optional]
We had developed a simple GUI for you to interact with the cordapp. You can access the website by using https://localhost:5000 or https://127.0.0.1:5000. The Flow Management Tool will automatically connect with the CorDapp running locally from your Corda cluster. You can test the connection by click on the dropdown list at the Flow Initiator section. You should be able to see the vNodes of your started CorDapp. You can easily trigger and query a Corda flow.



![image](https://github.com/corda/cordapp-template-kotlin/assets/51169685/88e6568e-49b4-46a8-b1e1-34140bcf03a9)


## Running the sample app
We will use the Chat app to demonstrate the functionalities of custom StateJSON and custom queries.

### Running the flows

In Corda 5, flows will be triggered via `POST /flow/{holdingidentityshorthash}` and flow result will need to be view at `GET /flow/{holdingidentityshorthash}/{clientrequestid}`
* holdingidentityshorthash: the id of the network participants, ie Bob, Alice, Charlie. You can view all the short hashes of the network member with another gradle task called `listVNodes`
* clientrequestid: the id you specify in the flow requestBody when you trigger a flow.

#### Step 1: Create Chat Entry from Alice
Pick a VNode identity to initiate the chat, and get its short hash. (Let's pick Alice. Dont pick Bob because Bob is the person who we will have the chat with).

Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body:
```
{
"clientRequestId": "create-1",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.CreateNewChatFlow",
"requestBody": {
"chatName":"Chat with Bob",
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
"message": "Hello Bob"
}
}
```

After trigger the create-chat flow, hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the short hash(Alice's hash) and clientrequestid to view the flow result

#### Step 2: View the Custom StateJson in Database
By using database tool such DBeaver or similar app, we connect to the postgre Database. Then, we locate to the Alice's vNode. We select
`utxo_visible_transaction_output` table, and we should be able to see the transaction record of Alice. We can see one column is called `custom_representation`, and
it will hold the custom StateJSON of the chat state that Alice created for Bob. For example,
```
{
"net.corda.v5.ledger.utxo.ContractState":{
"stateRef":"SHA-256D:3817854BD2DA35817360DFF00076DB9105DFF0F7B0824F4DF91F1BBA0E1039C4:0"
},
"com.r3.developers.customStateJSONandQuery.states.ChatState":{
"Id":"d47b10d2-f3bd-4571-8ed2-3ac651492874",
"chatName":"Chat with Bob",
"messageContent":"Hello Bob",
"messageContentFrom":"CN=Alice, OU=Test Dept, O=R3, L=London, C=GB"
}
}
```

#### Step 3: Create Chat Entry from Dave
In order to demonstrate the custom query functionality, we will need to create another chat entry to Bob from a different person. We will choose Dave,
```
{
"clientRequestId": "create-1",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.CreateNewChatFlow",
"requestBody": {
"chatName":"Chat with Bob from Dave",
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
"message": "Hello Bob from Dave"
}
}
```
Notice, the `chatName` and `message` are annotated with "from Dave".

#### Step 4: Perform Custom Query at Bob
Now, we will perform the custom query, we will select Bob to trigger the flow with the following requestBody. Note that the `ListChatsByCustomQueryFlow` is hard coded
to query only Alice's chat entries.
```
{
"clientRequestId": "customlist-1",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsByCustomQueryFlow",
"requestBody": {}
}
```


#### Step 5: Perform Generic Query at Bob
As a contrast, we will perform a generic query at Bob to query all the chat entries that Bob receives. This will print out all the chats that Bob has, including the message from Dave.
```
{
"clientRequestId": "list-1",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsFlow",
"requestBody": {}
}
```

Thus, we have concluded a full run through of the Custom StateJson and Query sample app.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ plugins {
}

allprojects {
group 'com.r3.developers.cordapptemplate'
group 'com.r3.developers.customStateJSONandQuery'
version '1.0-SNAPSHOT'

def javaVersion = VERSION_17
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.r3.developers.cordapptemplate.customStateJSONandQuery.contracts
package com.r3.developers.customStateJSONandQuery.contracts

import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.base.exceptions.CordaRuntimeException
import net.corda.v5.ledger.utxo.Command
import net.corda.v5.ledger.utxo.Contract
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.r3.developers.cordapptemplate.customStateJSONandQuery.states
package com.r3.developers.customStateJSONandQuery.states

import net.corda.v5.application.marshalling.JsonMarshallingService
import net.corda.v5.ledger.utxo.query.json.ContractStateVaultJsonFactory
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.r3.developers.cordapptemplate.customStateJSONandQuery.states
package com.r3.developers.customStateJSONandQuery.states

import com.r3.developers.cordapptemplate.customStateJSONandQuery.contracts.ChatContract
import com.r3.developers.customStateJSONandQuery.contracts.ChatContract
import net.corda.v5.base.types.MemberX500Name
import net.corda.v5.ledger.utxo.BelongsToContract
import net.corda.v5.ledger.utxo.ContractState
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.r3.developers.cordapptemplate.customeStateJSONandQuery.states
package com.r3.developers.customStateJSONandQuery.states

import net.corda.v5.ledger.utxo.query.VaultNamedQueryFactory
import net.corda.v5.ledger.utxo.query.registration.VaultNamedQueryBuilderFactory
Expand All @@ -7,13 +7,13 @@ class ChatCustomQueryFactory : VaultNamedQueryFactory {
override fun create(vaultNamedQueryBuilderFactory: VaultNamedQueryBuilderFactory) {
vaultNamedQueryBuilderFactory.create("GET_ALL_MSG")
.whereJson(
"WHERE visible_states.custom_representation ? 'com.r3.developers.cordapptemplate.utxoexample.states.ChatState' "
"WHERE visible_states.custom_representation ? 'com.r3.developers.customStateJSONandQuery.states.ChatState' "
)
.register()

vaultNamedQueryBuilderFactory.create("GET_MSG_FROM")
.whereJson(
"WHERE visible_states.custom_representation -> 'com.r3.developers.cordapptemplate.utxoexample.states.ChatState' ->> 'messageContentFrom' = :nameOfSender"
"WHERE visible_states.custom_representation -> 'com.r3.developers.customStateJSONandQuery.states.ChatState' ->> 'messageContentFrom' = :nameOfSender"
)
.register()
}
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts.ChatContract
import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.contracts.ChatContract
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.application.flows.*
import net.corda.v5.application.marshalling.JsonMarshallingService
import net.corda.v5.application.membership.MemberLookup
Expand Down Expand Up @@ -102,7 +102,7 @@ class CreateNewChatFlow: ClientStartableFlow {
RequestBody for triggering the flow via REST:
{
"clientRequestId": "create-1",
"flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.CreateNewChatFlow",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.CreateNewChatFlow",
"requestBody": {
"chatName":"Chat with Bob",
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.application.flows.*
import net.corda.v5.application.messaging.FlowMessaging
import net.corda.v5.application.messaging.FlowSession
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.application.flows.ClientRequestBody
import net.corda.v5.application.flows.ClientStartableFlow
import net.corda.v5.application.flows.CordaInject
Expand Down Expand Up @@ -108,7 +108,7 @@ class GetChatFlow: ClientStartableFlow {
RequestBody for triggering the flow via REST:
{
"clientRequestId": "get-1",
"flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.GetChatFlow",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.GetChatFlow",
"requestBody": {
"id":"** fill in id **",
"numberOfRecords":"4"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.application.flows.ClientRequestBody
import net.corda.v5.application.flows.ClientStartableFlow
import net.corda.v5.application.flows.CordaInject
Expand Down Expand Up @@ -37,7 +37,7 @@ class ListChatsByCustomQueryFlow : ClientStartableFlow {
.execute()

//from custom query to states, we can operate
val resultState = resultSet.results.map {it.state.contractState as ChatState}
val resultState = resultSet.results.map {it.state.contractState as ChatState }

//from the states -> human-readable.
val results = resultState.map {
Expand All @@ -60,7 +60,7 @@ class ListChatsByCustomQueryFlow : ClientStartableFlow {
RequestBody for triggering the flow via REST:
{
"clientRequestId": "customlist-1",
"flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.ListChatsByCustomQueryFlow",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsByCustomQueryFlow",
"requestBody": {}
}
*/
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.application.flows.ClientRequestBody
import net.corda.v5.application.flows.ClientStartableFlow
import net.corda.v5.application.flows.CordaInject
Expand Down Expand Up @@ -56,7 +56,7 @@ class ListChatsFlow : ClientStartableFlow {
RequestBody for triggering the flow via REST:
{
"clientRequestId": "list-1",
"flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.ListChatsFlow",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsFlow",
"requestBody": {}
}
*/
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.base.annotations.Suspendable
import net.corda.v5.base.exceptions.CordaRuntimeException
import net.corda.v5.base.types.MemberX500Name
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts.ChatContract
import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.contracts.ChatContract
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.application.flows.*
import net.corda.v5.application.marshalling.JsonMarshallingService
import net.corda.v5.application.membership.MemberLookup
Expand Down Expand Up @@ -483,7 +483,7 @@ class TestContractFlow: ClientStartableFlow {
/*
{
"clientRequestId": "dummy-1",
"flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.TestContractFlow",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.TestContractFlow",
"requestBody": {
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows
package com.r3.developers.customStateJSONandQuery.workflows

import com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts.ChatContract
import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState
import com.r3.developers.customStateJSONandQuery.contracts.ChatContract
import com.r3.developers.customStateJSONandQuery.states.ChatState
import net.corda.v5.application.flows.*
import net.corda.v5.application.marshalling.JsonMarshallingService
import net.corda.v5.application.membership.MemberLookup
Expand Down Expand Up @@ -100,7 +100,7 @@ class UpdateChatFlow: ClientStartableFlow {
RequestBody for triggering the flow via REST:
{
"clientRequestId": "update-2",
"flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.UpdateChatFlow",
"flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.UpdateChatFlow",
"requestBody": {
"id":"** fill in id **",
"message": "How are you today?"
Expand Down
Loading

0 comments on commit a1c3b94

Please sign in to comment.