SpringBoot: Flow Polling
This commit is contained in:
parent
5035a16930
commit
a0c91ee9ce
@ -3,6 +3,8 @@ package djmil.cordacheckers.cordaclient;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@ -12,25 +14,27 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
|
||||
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
|
||||
import djmil.cordacheckers.cordaclient.dao.VirtualNodeList;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.RequestBody;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.ResponseBody;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty;
|
||||
|
||||
@Service
|
||||
public class CordaClient {
|
||||
private final RestTemplate restTemplate;
|
||||
private final ObjectMapper jsonMapper;
|
||||
|
||||
public CordaClient(RestTemplate restTemplate) {
|
||||
public CordaClient(RestTemplate restTemplate, ObjectMapper jsonMapper) {
|
||||
//System.out.println("Creating REST Service");
|
||||
// this.restTemplate = restTemplateBuilder
|
||||
// .basicAuthentication("admin", "admin")
|
||||
// .build();
|
||||
this.restTemplate = restTemplate;
|
||||
this.jsonMapper = jsonMapper;
|
||||
}
|
||||
|
||||
public List<VirtualNode> getVirtualNodeList() {
|
||||
@ -59,41 +63,121 @@ public class CordaClient {
|
||||
* Obtain list of unconsumed (active) GameProposals
|
||||
* @param holdingIdentity
|
||||
* @return GameProposals list in JSON form
|
||||
* @throws JsonProcessingException
|
||||
*/
|
||||
public String listGameProposals(HoldingIdentity holdingIdentity) throws JsonProcessingException {
|
||||
// Request authorization header
|
||||
HttpHeaders headers = basicAuthorizationHeader();
|
||||
public String listGameProposals(HoldingIdentity holdingIdentity) {
|
||||
|
||||
|
||||
|
||||
RequestBody body = new RequestBody("list-2", "djmil.cordacheckers.gameproposal.ListFlow", new Empty());
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
final RequestBody requestBody = new RequestBody(
|
||||
"list-" + UUID.randomUUID(),
|
||||
"djmil.cordacheckers.gameproposal.ListFlow",
|
||||
new Empty()
|
||||
);
|
||||
|
||||
String json = mapper.writeValueAsString(body);
|
||||
final String gameProposalsJsonString = cordaFlowExecute(
|
||||
holdingIdentity,
|
||||
requestBody
|
||||
);
|
||||
|
||||
if (json != null)
|
||||
return json;
|
||||
// new String("{\n" + //
|
||||
// " \"clientRequestId\": \"list-1\",\n" + //
|
||||
// " \"flowClassName\": \"djmil.cordacheckers.gameproposal.ListFlow\",\n" + //
|
||||
// " \"requestBody\": {}\n" + //
|
||||
// "}");
|
||||
return gameProposalsJsonString;
|
||||
}
|
||||
|
||||
System.out.println("HiH "+holdingIdentity.shortHash()+"\nRequst JSON = "+json);
|
||||
private String cordaFlowExecute(HoldingIdentity holdingIdentity, RequestBody requestBody) {
|
||||
|
||||
try {
|
||||
final String requestBodyJson = this.jsonMapper.writeValueAsString(requestBody);
|
||||
|
||||
final ResponseBody startedFlow = cordaFlowPost(
|
||||
holdingIdentity,
|
||||
requestBodyJson
|
||||
);
|
||||
|
||||
final String flowExecutionResult = cordaFlowPoll(startedFlow);
|
||||
|
||||
// NOTE:
|
||||
// At this point, real production code, probably should convert data between CordaFlow
|
||||
// abstarction into ReactApp abstraction. Instead, to limit boring json shuffling, all
|
||||
// family of Corda.List flows were deliberatly designed to return frontend frendly JSONs.
|
||||
// At the same time, all other Corda flows, simply return plain text string with
|
||||
// operation result.
|
||||
return flowExecutionResult;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Unable to perform "+requestBody.flowClassName()
|
||||
+". Reason "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseBody cordaFlowPost(HoldingIdentity holdingIdentity, String requestBodyJson) {
|
||||
final HttpHeaders requestHeaders = basicAuthorizationHeader();
|
||||
|
||||
// Request
|
||||
final HttpEntity<String> request = new HttpEntity<>(json, headers);
|
||||
final HttpEntity<String> request = new HttpEntity<>(requestBodyJson, requestHeaders);
|
||||
|
||||
ResponseEntity<String> resp = this.restTemplate.exchange(
|
||||
"https://localhost:8888/api/v1/flow/"+holdingIdentity.shortHash(),
|
||||
final ResponseEntity<ResponseBody> responce = this.restTemplate.exchange(
|
||||
"https://localhost:8888/api/v1/flow/" + holdingIdentity.shortHash(),
|
||||
HttpMethod.POST,
|
||||
request,
|
||||
String.class);
|
||||
ResponseBody.class
|
||||
);
|
||||
|
||||
return resp.getBody();
|
||||
if (!responce.getStatusCode().is2xxSuccessful()) {
|
||||
throw new RuntimeException("CordaClient.startCordaFlow: unexpected request status "
|
||||
+responce.getStatusCode()) ;
|
||||
}
|
||||
|
||||
final ResponseBody responseBody = requireNonNull(
|
||||
responce.getBody(),
|
||||
"CordaClient.startCordaFlow: empty getBody()"
|
||||
);
|
||||
|
||||
if (!responseBody.isFlowStarted()) {
|
||||
throw new RuntimeException("CordaClient.startCordaFlow: Unexpected status: "
|
||||
+responseBody.flowStatus() + ": "
|
||||
+responseBody.flowError());
|
||||
}
|
||||
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
private String cordaFlowPoll(ResponseBody startedFlow) throws InterruptedException {
|
||||
final HttpHeaders requestHeaders = basicAuthorizationHeader();
|
||||
|
||||
final HttpEntity<String> request = new HttpEntity<>(requestHeaders);
|
||||
|
||||
for (int retry = 0; retry < 6; retry++) {
|
||||
// Give Corda cluster some time to process our request
|
||||
TimeUnit.SECONDS.sleep(retry*retry +1); // 1 2 5 8 17 33 sec
|
||||
|
||||
final ResponseEntity<ResponseBody> responce = this.restTemplate.exchange(
|
||||
"https://localhost:8888/api/v1/flow/"
|
||||
+ startedFlow.holdingIdentityShortHash()+"/"
|
||||
+ startedFlow.clientRequestId(),
|
||||
HttpMethod.GET,
|
||||
request,
|
||||
ResponseBody.class
|
||||
);
|
||||
|
||||
if (responce.getStatusCode() != HttpStatus.OK) {
|
||||
throw new RuntimeException("CordaClient.cordaFlowPoll: unexpected request status "
|
||||
+responce.getStatusCode()) ;
|
||||
}
|
||||
|
||||
final ResponseBody responseBody = requireNonNull(
|
||||
responce.getBody(),
|
||||
"CordaClient.cordaFlowPoll: empty getBody()"
|
||||
);
|
||||
|
||||
if (responseBody.isFlowCompleted() && responseBody.flowResult() != null) {
|
||||
System.out.println("Completed "+responseBody.flowResult());
|
||||
return responseBody.flowResult();
|
||||
} else
|
||||
if (responseBody.flowError() != null) {
|
||||
return "Flow execution error: " +responseBody.flowError();
|
||||
} else
|
||||
if (!responseBody.isFlowRunning()) {
|
||||
return "Unexpect ResponseBody status: " +responseBody.flowStatus();
|
||||
}
|
||||
}
|
||||
|
||||
return "CordaClient.cordaFlowPoll: retry limit";
|
||||
}
|
||||
|
||||
private HttpHeaders basicAuthorizationHeader() {
|
||||
|
@ -8,4 +8,18 @@ public record ResponseBody(
|
||||
String flowResult,
|
||||
String flowError,
|
||||
String timestamp
|
||||
) { }
|
||||
) {
|
||||
|
||||
public boolean isFlowStarted() {
|
||||
return this.flowStatus.equals("START_REQUESTED");
|
||||
}
|
||||
|
||||
public boolean isFlowRunning() {
|
||||
return this.flowStatus.equals("RUNNING");
|
||||
}
|
||||
|
||||
public boolean isFlowCompleted() {
|
||||
return this.flowStatus.equals("COMPLETED");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ public class CordaClientTest {
|
||||
|
||||
@Test
|
||||
void testListGameProposals() throws JsonProcessingException {
|
||||
String resp = cordaClient.listGameProposals(holdingIdentityResolver.getByCommonName("alice"));
|
||||
String resp = cordaClient.listGameProposals(
|
||||
holdingIdentityResolver.getByCommonName("alice"));
|
||||
|
||||
System.out.println("testListGameProposals "+ resp);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user