diff --git a/convex-core/src/main/java/convex/core/ErrorCodes.java b/convex-core/src/main/java/convex/core/ErrorCodes.java index 6c1a1b88a..ebd47de25 100644 --- a/convex-core/src/main/java/convex/core/ErrorCodes.java +++ b/convex-core/src/main/java/convex/core/ErrorCodes.java @@ -238,5 +238,10 @@ public class ErrorCodes { */ public static final Keyword MISSING = Keyword.intern("MISSING"); + /** + * Error code indicating a resource limit exceeded + */ + public static final Keyword LIMIT = Keyword.intern("LIMIT"); + } diff --git a/convex-core/src/main/java/convex/core/Result.java b/convex-core/src/main/java/convex/core/Result.java index af35836f7..b04a57a02 100644 --- a/convex-core/src/main/java/convex/core/Result.java +++ b/convex-core/src/main/java/convex/core/Result.java @@ -68,6 +68,11 @@ private Result(AVector values) { super(RESULT_FORMAT, values); } + /** + * Build a Result from a vector. WARNING: does not validate values + * @param values + * @return + */ public static Result buildFromVector(AVector values) { return new Result(values); } @@ -466,6 +471,12 @@ public HashMap toJSON() { return hm; } + /** + * Construct a result from a cell of data + * @param data Result or Map including Result fields + * @return Result value + * @throws IllegalArgumentException if data is of incorrect type + */ public static Result fromData(ACell data) { if (data instanceof Result) { return (Result) data; diff --git a/convex-core/src/main/java/convex/core/SourceCodes.java b/convex-core/src/main/java/convex/core/SourceCodes.java index 3b2edc8cd..d2da2839e 100644 --- a/convex-core/src/main/java/convex/core/SourceCodes.java +++ b/convex-core/src/main/java/convex/core/SourceCodes.java @@ -20,6 +20,11 @@ public class SourceCodes { */ public static final Keyword COMM = Keyword.intern("COMM"); + /** + * Source code indicating an error at the server side (e.g. REST Server) + */ + public static final Keyword SERVER = Keyword.intern("SERVER"); + /** * Source code indicating a failure during peer handling */ @@ -43,4 +48,5 @@ public class SourceCodes { + } diff --git a/convex-core/src/main/java/convex/core/data/Strings.java b/convex-core/src/main/java/convex/core/data/Strings.java index 313e27ccd..292bb9c37 100644 --- a/convex-core/src/main/java/convex/core/data/Strings.java +++ b/convex-core/src/main/java/convex/core/data/Strings.java @@ -60,6 +60,8 @@ public class Strings { public static final StringShort OLD_SEQUENCE = StringShort.create("Old sequence number"); + public static final StringShort PRINT_EXCEEDED = StringShort.create(Constants.PRINT_EXCEEDED_STRING); + /** * Reads a String from a Blob encoding. diff --git a/convex-java/src/main/java/convex/java/ConvexHTTP.java b/convex-java/src/main/java/convex/java/ConvexHTTP.java index 4267f28ca..59c1e1d46 100644 --- a/convex-java/src/main/java/convex/java/ConvexHTTP.java +++ b/convex-java/src/main/java/convex/java/ConvexHTTP.java @@ -82,7 +82,8 @@ public CompletableFuture query(ACell form, Address address) { CompletableFuture result=future.thenApply(response->{ String body=response.getBodyText(); try { - System.out.println(body); + // System.out.println(body); + // We expect a map containing the result fields ACell data=Reader.read(body); return Result.fromData(data); } catch (ParseException e) { 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 5c2829312..ca0e6a19a 100644 --- a/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java +++ b/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java @@ -13,12 +13,14 @@ import convex.core.Coin; import convex.core.ErrorCodes; import convex.core.Result; +import convex.core.SourceCodes; import convex.core.crypto.AKeyPair; import convex.core.crypto.ASignature; import convex.core.crypto.Ed25519Signature; import convex.core.data.ABlob; import convex.core.data.ACell; import convex.core.data.AMap; +import convex.core.data.AString; import convex.core.data.AccountKey; import convex.core.data.AccountStatus; import convex.core.data.Address; @@ -33,6 +35,7 @@ import convex.core.data.PeerStatus; import convex.core.data.Ref; import convex.core.data.SignedData; +import convex.core.data.Strings; import convex.core.data.prim.AInteger; import convex.core.data.prim.CVMLong; import convex.core.exceptions.BadFormatException; @@ -651,8 +654,6 @@ public void runQuery(Context ctx) throws InterruptedException { } else { Map req = getJSONBody(ctx); addr = Address.parse(req.get("address")); - if (addr == null) - throw new BadRequestResponse("query requires an 'address' field."); Object srcValue = req.get("source"); form = readCode(srcValue); } @@ -662,35 +663,43 @@ public void runQuery(Context ctx) throws InterruptedException { } private void prepareResult(Context ctx, Result r) { + if (r.getSource()==null) { + r=r.withSource(SourceCodes.SERVER); + } + ctx.status(r.isError()?422:200); Enumeration accepts=ctx.req().getHeaders("Accept"); - String accept=ContentTypes.JSON; + String type=ContentTypes.JSON; if (accepts!=null) { for (String a:Collections.list(accepts)) { if (a.contains(ContentTypes.CVX_RAW)) { - accept=ContentTypes.CVX_RAW; + type=ContentTypes.CVX_RAW; break; } if (a.contains(ContentTypes.CVX)) { - accept=ContentTypes.CVX; + type=ContentTypes.CVX; } } } - if (accept.equals(ContentTypes.JSON)) { + if (type.equals(ContentTypes.JSON)) { ctx.contentType(ContentTypes.JSON); HashMap resultJSON = r.toJSON(); ctx.result(JSON.toPrettyString(resultJSON)); - } else if (accept.equals(ContentTypes.CVX)) { + } else if (type.equals(ContentTypes.CVX)) { ctx.contentType(ContentTypes.CVX); - ctx.result(RT.print(r).toString()); - } else if (accept.equals(ContentTypes.CVX_RAW)) { + AString rs=RT.print(r); + if (rs==null) { + rs=RT.print(Result.error(ErrorCodes.LIMIT, Strings.PRINT_EXCEEDED).withSource(SourceCodes.PEER)); + } + ctx.result(rs.toString()); + } else if (type.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); + ctx.result("Unsupported content type: "+type); } } diff --git a/convex-restapi/src/test/java/convex/restapi/test/ConvexHTTPTest.java b/convex-restapi/src/test/java/convex/restapi/test/ConvexHTTPTest.java index 8d446785f..162ccd6d7 100644 --- a/convex-restapi/src/test/java/convex/restapi/test/ConvexHTTPTest.java +++ b/convex-restapi/src/test/java/convex/restapi/test/ConvexHTTPTest.java @@ -2,13 +2,18 @@ import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.net.URI; import java.net.URISyntaxException; +import convex.core.Result; +import convex.core.data.prim.CVMLong; import convex.core.exceptions.ResultException; import convex.core.init.Init; +import convex.core.lang.Reader; import convex.core.util.Utils; import convex.java.ConvexHTTP; @@ -24,9 +29,13 @@ private ConvexHTTP connect() { } } - @Test public void testQuery() throws ResultException { + @Test public void testQuery() throws ResultException, InterruptedException { ConvexHTTP convex=connect(); Long l=convex.getBalance(); assertNotNull(l); + + Result r=convex.querySync(Reader.read("(+ 2 3)"), null); + assertFalse(r.isError()); + assertEquals(CVMLong.create(5),r.getValue()); } }