From 213ed5272812318b891b73ea97ec858881b54047 Mon Sep 17 00:00:00 2001 From: mikera Date: Sat, 21 Sep 2024 18:01:32 +0100 Subject: [PATCH] More HTTP refactoring for different content types --- .../src/main/java/convex/java/Convex.java | 2 - .../main/java/convex/api/ContentTypes.java | 27 +++++++ .../java/convex/restapi/api/ChainAPI.java | 78 ++++++++++--------- 3 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 convex-peer/src/main/java/convex/api/ContentTypes.java diff --git a/convex-java/src/main/java/convex/java/Convex.java b/convex-java/src/main/java/convex/java/Convex.java index 2ce171a46..cdc86fe4f 100644 --- a/convex-java/src/main/java/convex/java/Convex.java +++ b/convex-java/src/main/java/convex/java/Convex.java @@ -3,12 +3,10 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; -import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import convex.core.ErrorCodes; diff --git a/convex-peer/src/main/java/convex/api/ContentTypes.java b/convex-peer/src/main/java/convex/api/ContentTypes.java new file mode 100644 index 000000000..f8649ae72 --- /dev/null +++ b/convex-peer/src/main/java/convex/api/ContentTypes.java @@ -0,0 +1,27 @@ +package convex.api; + +/** + * MIME content types used for peer-to=peer communication + */ +public class ContentTypes { + + /** + * Content type for plain test + */ + public static final String TEXT="text/plain"; + + /** + * Content type for JSON + */ + public static final String JSON="application/json"; + + /** + * Content type for CVX Readable format + */ + public static final String CVX="application/cvx"; + + /** + * Content type for CVX raw encoding + */ + public static final String CVX_RAW="application/cvx-raw"; +} diff --git a/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java b/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java index 128cfcbc1..15cef9e00 100644 --- a/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java +++ b/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java @@ -1,11 +1,14 @@ package convex.restapi.api; import java.io.IOException; +import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import convex.api.ContentTypes; import convex.api.Convex; import convex.core.Coin; import convex.core.ErrorCodes; @@ -98,7 +101,12 @@ public void addRoutes(Javalin app) { summary = "Get data from the server with the specified hash", operationId = "data", pathParams = { - @OpenApiParam(name = "hash", description = "Data hash as a hex string. Leading '0x' is optional but discouraged.", required = true, type = String.class, example = "0x1234567812345678123456781234567812345678123456781234567812345678") }) + @OpenApiParam( + name = "hash", + description = "Data hash as a hex string. Leading '0x' is optional but discouraged.", + required = true, + type = String.class, + example = "0x1234567812345678123456781234567812345678123456781234567812345678") }) public void getData(Context ctx) { String hashParam = ctx.pathParam("hash"); Hash h = Hash.parse(hashParam); @@ -202,7 +210,7 @@ public void queryAccount(Context ctx) throws InterruptedException { throw new BadRequestResponse(jsonError("Invalid address: " + addrParam)); } - Result r = doQuery(Lists.of(Symbols.ACCOUNT, addr)); + Result r = convex.querySync(Lists.of(Symbols.ACCOUNT, addr)); if (r.isError()) { prepareResult(ctx,r); @@ -241,7 +249,7 @@ public void queryPeer(Context ctx) throws InterruptedException { throw new BadRequestResponse(jsonError("Invalid peer key: " + addrParam)); } - Result r = doQuery(Reader.read("(get-in *state* [:peers " + addr + "])")); + Result r = convex.querySync(Reader.read("(get-in *state* [:peers " + addr + "])")); if (r.isError()) { prepareResult(ctx,r); @@ -258,33 +266,6 @@ public void queryPeer(Context ctx) throws InterruptedException { ctx.result(JSON.toPrettyString(hm)); } - /** - * Runs a query, wrapping exceptions - * - * @param form - * @return - * @throws InterruptedException - */ - private Result doQuery(ACell form) throws InterruptedException { - return convex.querySync(form); - } - - /** - * Runs a transaction, wrapping exceptions - * - * @param form - * @return - */ - private Result doTransaction(SignedData signedTransaction) { - try { - return convex.transactSync(signedTransaction); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new InternalServerErrorResponse("Failed to execute transaction: " + e); - } - } - - private static Keyword K_FAUCET=Keyword.create("faucet"); @OpenApi(path = ROUTE + "faucet", @@ -516,7 +497,7 @@ public void runTransact(Context ctx) throws InterruptedException, IOException { AKeyPair kp = AKeyPair.create(seed.toFlatBlob()); SignedData sd = kp.signData(trans); - Result r = doTransaction(sd); + Result r = convex.transactSync(sd); prepareResult(ctx,r); } @@ -559,7 +540,7 @@ private static ACell readCode(Object srcValue) { description = "Transaction service unavailable" ) } ) - public void runTransactionSubmit(Context ctx) { + public void runTransactionSubmit(Context ctx) throws InterruptedException { Map req = getJSONBody(ctx); // Get the transaction hash @@ -607,7 +588,7 @@ public void runTransactionSubmit(Context ctx) { ASignature sig = Ed25519Signature.fromBlob(sigData); SignedData sd = SignedData.create(key, sig, trans.getRef()); - Result r = doTransaction(sd); + Result r = convex.transactSync(sd); prepareResult(ctx,r); } @@ -664,9 +645,36 @@ public void runQuery(Context ctx) throws InterruptedException { } private void prepareResult(Context ctx, Result r) { - HashMap resultJSON = r.toJSON(); ctx.status(r.isError()?422:200); - ctx.result(JSON.toPrettyString(resultJSON)); + Enumeration accepts=ctx.req().getHeaders("Accept"); + String accept=ContentTypes.JSON; + if (accepts!=null) { + for (String a:Collections.list(accepts)) { + if (a.contains(ContentTypes.CVX_RAW)) { + accept=ContentTypes.CVX_RAW; + break; + } + if (a.contains(ContentTypes.CVX)) { + accept=ContentTypes.CVX; + } + } + } + + if (accept.equals(ContentTypes.JSON)) { + ctx.contentType(ContentTypes.JSON); + HashMap resultJSON = r.toJSON(); + ctx.result(JSON.toPrettyString(resultJSON)); + } else if (accept.equals(ContentTypes.CVX)) { + ctx.contentType(ContentTypes.CVX); + ctx.result(RT.print(r).toString()); + } else if (accept.equals(ContentTypes.CVX_RAW)) { + ctx.contentType(ContentTypes.CVX_RAW); + ctx.result(Format.encodeMultiCell(r, true).getBytes()); + } else { + ctx.contentType(ContentTypes.TEXT); + ctx.status(400); + ctx.result("Unsupported content type: "+accept); + } } }