feat: add Java SDK language support#1477
Conversation
Greptile SummaryThis PR adds a full Java SDK language target to the appwrite SDK generator, including a PHP language class, Twig templates (client, services, models, enums, query/operator helpers, pom.xml), and PHPUnit tests for Java 11 and 17 via Docker. Many of the critical compile and runtime bugs identified in previous review rounds have been addressed in the current revision: the IIFE lambda pattern now correctly casts to Confidence Score: 3/5The PR is substantially improved over previous rounds, but several production code paths (generic model responses, nested-query serialization key ordering) remain unverified by the test suite. The major compile and runtime bugs identified in earlier review rounds (IIFE lambda, binary response corruption, chunked-upload correctness, resume-upload NPE, spatial query helpers, sentinel-value defaults, logging-interceptor dependency) all appear to be fixed in the current revision. No new confirmed P0/P1 issues were found. The remaining uncertainty is that the mock test spec does not exercise generic-model responses (Document) or run the or/and/elemMatch assertions against a live server, so those serialization paths cannot be fully validated without running the generated SDK against the real Appwrite API. This uncertainty prevents a higher score. templates/java/src/main/java/io/appwrite/Query.java.twig (nested-query key ordering), templates/java/src/main/java/io/appwrite/services/ServiceTemplate.java.twig (generic-model converter cast) Important Files Changed
Reviews (22): Last reviewed commit: "fix(java): use wall-clock nanoseconds fo..." | Re-trigger Greptile |
- Query: add spatial/geo methods (distanceEqual/NotEqual/GreaterThan/LessThan with distance+meters fields, intersects/notIntersects/crosses/notCrosses/overlaps/notOverlaps/touches/notTouches, elemMatch) - Client: null-check HttpUrl.parse() result to prevent NPE on bad URLs - Client: fix last-chunk data corruption by using Arrays.copyOfRange/readFully instead of fixed CHUNK_SIZE buffer - Client: replace FileInputStream resource leak with Files.readAllBytes(Paths.get(...)) - Tests: fix AppwriteException unwrap depth (ExecutionException -> RuntimeException -> AppwriteException requires two getCause() calls) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Client: read byte[] responses with body().bytes() to avoid charset re-encoding that corrupts binary data - ServiceTemplate: only generate no-optional-args overload when method has both required AND optional params; avoids empty-arg overload when all params are optional Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Switch test Docker images from eclipse-temurin (JDK only) to maven:3.9-eclipse-temurin-11/17 which bundles both JDK and Maven - Fix trailing comma in overload method signature by iterating only required params using Twig filter instead of relying on loop.last over the full parameter list
…mbdas
- Query: replace List.copyOf(List<String>) with new ArrayList<Object>(...)
for or/and/elemMatch/exists/notExists/select to fix generics invariance
compile error
- Model/RequestModel: replace bare (() -> {}).call() with
((Supplier<T>) () -> {}).get() — untyped IIFE lambdas are invalid Java
(valid Kotlin only); affects sub_schema array and enum array properties
in from() and toMap()
…aded Prevents NPE from converter.apply(null) when resuming an upload that is already complete (offset >= size means the while loop never runs)
…ator Java generics are invariant so List<String>/List<Double> cannot be assigned to List<Object>. Tests.java passes typed lists (List.of(...)) to containsAny, containsAll, all spatial/distance methods in Query, and arrayAppend/arrayPrepend/arrayIntersect/arrayDiff in Operator. Changed all public method signatures to List<?> and widen internally with new ArrayList<Object>(values).
…ection - Tests: redirect() returns CompletableFuture<String> not Map; remove incorrect cast that caused ClassCastException at runtime - Java.php: change TYPE_INTEGER optional default from -1L to null so callers can distinguish unset from a valid negative value - Client: check extraHeaders first then fall back to global headers map for content-type detection instead of ignoring global headers
Mirrors the Long fix — 1.0 is a valid API value and must not be sent silently when the caller omits the parameter.
Empty string and false are valid API values; using them as defaults causes unintended parameters to be sent on every call that omits them. Consistent with the null fix already applied to Long and Double.
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
… from() - Client: check instanceof MultipartBody.Part instead of hardcoded key name 'file' so any upload param name works correctly - Model: add array-of-enum branch before the scalar-enum branch in from() so List<EnumType> fields deserialize correctly instead of emitting a Supplier<EnumType> compile error
…odel For List<EnumType> properties, iterate and call .getValue() on each element. Without this branch the scalar-enum path called .getValue() on the List itself, causing a compile error.
…ies constructor When a model has additionalProperties (generic T data param), the last regular property was missing its trailing comma, causing a compile error for all generic response models like Document<T>.
…tance queries Remove top-level Gson-annotated distance/meters fields. Instead append them to the values list via withDistance() helpers so the serialized JSON matches the expected [[coords],distance,meters] structure that QUERY_HELPER_RESPONSES asserts.
CHUNK_SIZE and chunkLen diverge on the final chunk. Use chunkLen so Content-Range header reflects actual bytes sent and offset advances by the exact number of bytes read, not a fixed block size.
…e GET - Remove catch(AppwriteException e) from call() — AppwriteException is only ever wrapped in RuntimeException inside the try body, so the catch clause was an unreachable checked-exception compile error - Wrap the chunkedUpload resume GET in try-catch so a 404 on brand-new uploads is silently ignored and the upload starts from offset 0
Storing raw JSON strings in the values list causes Gson to re-serialize them as escaped strings. Parse each query string back to Object via Gson first so they serialize as nested JSON objects.
… order gson.fromJson returns a LinkedTreeMap with unpredictable key ordering, causing re-serialized nested queries to mismatch expected output. JsonParser.parseString returns a JsonElement that Gson serializes verbatim, preserving the original key order.
intersects/notIntersects/crosses/notCrosses/overlaps/notOverlaps/ touches/notTouches were storing flat coords [lat,lon] but expected output is [[lat,lon]]. Use wrapCoords() to nest the input list as a single values element.
…withDistance Previous format spread coords+distance+meters as flat values elements. Expected format is values:[[[coords],distance,meters]] — the entire triplet as one nested element. Build an inner list [coords,distance,meters] and wrap it in an outer single-element list.
System.nanoTime() is a monotonic clock unrelated to wall time, giving wrong sub-second values. now.getNano()/1000 gives 0-999999 microseconds from the wall clock, correctly filling the 5-hex-digit field and preserving time-sortability of generated IDs.
|
Hi team! I've addressed all the issues flagged by the review bot across several rounds — no P0/P1 issues remain. Would love to get maintainer feedback on whether Java SDK support fits the project, and if there's anything else needed before this can move forward. Thanks! |
What does this PR do?
Adds a new Java SDK language target to the SDK generator.
Files added:
Object> for generic models
Files modified:
Test files added:
Test Plan
Generate the SDK locally:
php example.php java
Inspect examples/java/ — all .java source files, enums, models, services, docs, and pom.xml should be present.
Run the test suite (requires Docker):$(pwd):$ (pwd):rw -w $(pwd)
docker run --rm -v
-v /var/run/docker.sock:/var/run/docker.sock
php:8.3-cli-alpine sh -c
"apk add docker-cli && vendor/bin/phpunit --filter JavaJava11Test"
Lint Twig templates:
composer lint-twig
Related PRs and Issues
No existing issue — this is a new language addition following the process described in CONTRIBUTING.md.
Have you read the https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md?
Yes. The implementation follows the SDK checklist in CONTRIBUTING.md:
request/response parsing, and AppwriteException on error