From bdf9a22dfaa50d83c26cacde6b6c96d6b792a9a5 Mon Sep 17 00:00:00 2001 From: GoodrichDev Date: Tue, 8 Sep 2026 15:24:07 -0700 Subject: [PATCH] Fix nearby OpenAPI path and request wrapper; share coordinate validation to reject malformed values, fractions, and overflow. --- .../emcapi/endpoint/LocationEndpoint.java | 23 +------------------ .../emcapi/endpoint/NearbyEndpoint.java | 17 ++++++++------ .../net/earthmc/emcapi/util/JSONUtil.java | 20 ++++++++++++++++ 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/earthmc/emcapi/endpoint/LocationEndpoint.java b/src/main/java/net/earthmc/emcapi/endpoint/LocationEndpoint.java index 8635d3b..6e07669 100644 --- a/src/main/java/net/earthmc/emcapi/endpoint/LocationEndpoint.java +++ b/src/main/java/net/earthmc/emcapi/endpoint/LocationEndpoint.java @@ -1,11 +1,9 @@ package net.earthmc.emcapi.endpoint; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.palmergames.bukkit.towny.TownyAPI; import com.palmergames.bukkit.towny.object.Town; -import io.javalin.http.BadRequestResponse; import io.javalin.openapi.ContentType; import io.javalin.openapi.HttpMethod; import io.javalin.openapi.OpenApi; @@ -79,26 +77,7 @@ public LocationEndpoint(final EMCAPI plugin) { @Override public Pair getObjectOrNull(@NotNull JsonElement element, @Nullable String key) { - JsonArray jsonArray = JSONUtil.getJsonElementAsJsonArrayOrNull(element); - if (jsonArray == null) throw new BadRequestResponse("Your query contains a value that is not a JSON array"); - - int x; - int z; - try { - JsonElement xElement = jsonArray.get(0); - JsonElement zElement = jsonArray.get(1); - - Integer xInner = JSONUtil.getJsonElementAsIntegerOrNull(xElement); - Integer zInner = JSONUtil.getJsonElementAsIntegerOrNull(zElement); - if (xInner == null || zInner == null) throw new BadRequestResponse("A JSON array in your query contained a value that was not an int"); - - x = xInner; - z = zInner; - } catch (IndexOutOfBoundsException oobe) { - throw new BadRequestResponse("A JSON array in your query did not contain two values"); - } - - return new Pair<>(x, z); + return JSONUtil.parseCoordinates(element); } @Override diff --git a/src/main/java/net/earthmc/emcapi/endpoint/NearbyEndpoint.java b/src/main/java/net/earthmc/emcapi/endpoint/NearbyEndpoint.java index af578ac..c3c5358 100644 --- a/src/main/java/net/earthmc/emcapi/endpoint/NearbyEndpoint.java +++ b/src/main/java/net/earthmc/emcapi/endpoint/NearbyEndpoint.java @@ -15,6 +15,7 @@ import io.javalin.openapi.HttpMethod; import io.javalin.openapi.OpenApi; import io.javalin.openapi.OpenApiContent; +import io.javalin.openapi.OpenApiContentProperty; import io.javalin.openapi.OpenApiRequestBody; import io.javalin.openapi.OpenApiResponse; import kotlin.Pair; @@ -35,23 +36,27 @@ import java.util.Objects; @OpenApi( - path = "/v4/location", + path = "/v4/nearby", methods = HttpMethod.POST, - summary = "Query location data", + summary = "Find nearby towns or nations", requestBody = @OpenApiRequestBody( required = true, description = "Target and type, search type, radius", content = { @OpenApiContent( - from = ContentTypes.NearbyQuery.class, + properties = @OpenApiContentProperty(name = "query", from = ContentTypes.NearbyQuery.class, isArray = true), mimeType = ContentType.JSON, example = """ { + "query": [ + { "target_type": "TOWN", "target": "Melbourne", "search_type": "TOWN", "radius": 100, "strict": true + } + ] } """ ) @@ -60,6 +65,7 @@ responses = { @OpenApiResponse( status = "200", + description = "An array containing one array of matching name/UUID objects per query", content = { @OpenApiContent( from = ContentTypes.NameUUID[].class, @@ -125,10 +131,7 @@ public NearbyContext getObjectOrNull(@NotNull JsonElement element, @Nullable Str JsonElement targetElement = jsonObject.get("target"); return switch (targetType) { case COORDINATE -> { - JsonArray jsonArray = JSONUtil.getJsonElementAsJsonArrayOrNull(targetElement); - if (jsonArray == null) throw new BadRequestResponse("Your target is not a valid JSON array"); - - Pair pair = new Pair<>(jsonArray.get(0).getAsInt(), jsonArray.get(1).getAsInt()); + Pair pair = JSONUtil.parseCoordinates(targetElement); yield new NearbyContext(targetType, pair, searchType, radius, strict); } diff --git a/src/main/java/net/earthmc/emcapi/util/JSONUtil.java b/src/main/java/net/earthmc/emcapi/util/JSONUtil.java index c9dd6f4..b4f36db 100644 --- a/src/main/java/net/earthmc/emcapi/util/JSONUtil.java +++ b/src/main/java/net/earthmc/emcapi/util/JSONUtil.java @@ -7,6 +7,7 @@ import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import io.javalin.http.BadRequestResponse; +import kotlin.Pair; public class JSONUtil { @@ -40,6 +41,25 @@ public static Integer getJsonElementAsIntegerOrNull(JsonElement element) { return primitive.getAsInt(); } + public static Pair parseCoordinates(JsonElement element) { + JsonArray array = getJsonElementAsJsonArrayOrNull(element); + if (array == null || array.size() != 2) { + throw new BadRequestResponse("Coordinates must be an array of two integers: [x, z]"); + } + + for (JsonElement coordinate : array) { + if (!coordinate.isJsonPrimitive() || !coordinate.getAsJsonPrimitive().isNumber()) { + throw new BadRequestResponse("Coordinates must contain only integers"); + } + } + + try { + return new Pair<>(array.get(0).getAsBigDecimal().intValueExact(), array.get(1).getAsBigDecimal().intValueExact()); + } catch (ArithmeticException | NumberFormatException e) { + throw new BadRequestResponse("Coordinates must be whole numbers within the 32-bit integer range"); + } + } + public static Boolean getJsonElementAsBooleanOrNull(JsonElement element) { if (element == null) return null;