From 83941e4b77783b47d76ecd3ce6b2032102ae4189 Mon Sep 17 00:00:00 2001 From: Movindu Jayathilake Date: Tue, 30 Jun 2026 13:07:03 +0530 Subject: [PATCH 1/3] Add render template support --- .code-samples.meilisearch.yaml | 25 ++++++ src/main/java/com/meilisearch/sdk/Client.java | 12 +++ .../sdk/RenderTemplateRequest.java | 30 +++++++ .../sdk/model/RenderTemplateResult.java | 13 +++ .../sdk/RenderTemplateRequestTest.java | 86 +++++++++++++++++++ 5 files changed, 166 insertions(+) create mode 100644 src/main/java/com/meilisearch/sdk/RenderTemplateRequest.java create mode 100644 src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java create mode 100644 src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index b5b82ec5..f6423f99 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -640,3 +640,28 @@ webhooks_patch_1: |- Webhook updated_webhook = this.client.updateWebhook(webhook.getUuid(), webhookReq2); webhooks_delete_1: |- this.client.deleteWebhook("WEBHOOK_UUID"); + + +post_render_template_1: |- + Map features = new HashMap<>(); + features.put("renderRoute", true); + client.experimentalFeatures(features); + + Map template = new HashMap<>(); + template.put("kind", "inlineDocumentTemplate"); + template.put("inline", "Product {{doc.name}} is a {{doc.color}} {{doc.category}}."); + + Map product = new HashMap<>(); + product.put("name", "Nike Air Max"); + product.put("color", "Black"); + product.put("category", "Shoes"); + + Map input = new HashMap<>(); + input.put("kind", "inlineDocument"); + input.put("inline", product); + + RenderTemplateRequest request = new RenderTemplateRequest() + .setTemplate(template) + .setInput(input); + + client.renderTemplate(request); diff --git a/src/main/java/com/meilisearch/sdk/Client.java b/src/main/java/com/meilisearch/sdk/Client.java index 37e250eb..3f4bfed5 100644 --- a/src/main/java/com/meilisearch/sdk/Client.java +++ b/src/main/java/com/meilisearch/sdk/Client.java @@ -482,6 +482,18 @@ public void experimentalFeatures(Map features) { this.config.httpClient.patch("/experimental-features", features, Void.class); } + /** + * Renders a template using the experimental render-template route. + * + * @param request Render template request body + * @return Rendered template result + * @throws MeilisearchException if an error occurs + */ + public RenderTemplateResult renderTemplate(RenderTemplateRequest request) + throws MeilisearchException { + return this.config.httpClient.post("/render-template", request, RenderTemplateResult.class); + } + public String generateTenantToken(String apiKeyUid, Map searchRules) throws MeilisearchException { return this.generateTenantToken(apiKeyUid, searchRules, new TenantTokenOptions()); diff --git a/src/main/java/com/meilisearch/sdk/RenderTemplateRequest.java b/src/main/java/com/meilisearch/sdk/RenderTemplateRequest.java new file mode 100644 index 00000000..1ab79b1d --- /dev/null +++ b/src/main/java/com/meilisearch/sdk/RenderTemplateRequest.java @@ -0,0 +1,30 @@ +package com.meilisearch.sdk; + +import java.util.Map; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.json.JSONObject; + +@Builder +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@Getter +@Setter +@Accessors(chain = true) +public class RenderTemplateRequest { + private Map template; + private Map input; + + public RenderTemplateRequest() {} + + @Override + public String toString() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("template", this.template); + jsonObject.putOpt("input", this.input); + return jsonObject.toString(); + } +} diff --git a/src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java b/src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java new file mode 100644 index 00000000..35b147f8 --- /dev/null +++ b/src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java @@ -0,0 +1,13 @@ +package com.meilisearch.sdk.model; + +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public class RenderTemplateResult { + Object template; + Object rendered; + + public RenderTemplateResult() {} +} diff --git a/src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java b/src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java new file mode 100644 index 00000000..e5c440b5 --- /dev/null +++ b/src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java @@ -0,0 +1,86 @@ +package com.meilisearch.sdk; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +import java.util.HashMap; +import java.util.Map; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +class RenderTemplateRequestTest { + @Test + void toStringWithTemplateAndInput() { + Map template = new HashMap<>(); + template.put("kind", "inlineDocumentTemplate"); + template.put("inline", "Product {{doc.name}} is a {{doc.color}} {{doc.category}}."); + + Map product = new HashMap<>(); + product.put("name", "Nike Air Max"); + product.put("color", "Black"); + product.put("category", "Shoes"); + + Map input = new HashMap<>(); + input.put("kind", "inlineDocument"); + input.put("inline", product); + + RenderTemplateRequest request = + new RenderTemplateRequest().setTemplate(template).setInput(input); + + JSONObject json = new JSONObject(request.toString()); + + assertThat( + json.getJSONObject("template").getString("kind"), + is(equalTo("inlineDocumentTemplate"))); + assertThat( + json.getJSONObject("template").getString("inline"), + is(equalTo("Product {{doc.name}} is a {{doc.color}} {{doc.category}}."))); + + assertThat(json.getJSONObject("input").getString("kind"), is(equalTo("inlineDocument"))); + assertThat( + json.getJSONObject("input").getJSONObject("inline").getString("name"), + is(equalTo("Nike Air Max"))); + assertThat( + json.getJSONObject("input").getJSONObject("inline").getString("color"), + is(equalTo("Black"))); + assertThat( + json.getJSONObject("input").getJSONObject("inline").getString("category"), + is(equalTo("Shoes"))); + } + + @Test + void toStringWithoutInput() { + Map template = new HashMap<>(); + template.put("kind", "inlineDocumentTemplate"); + template.put("inline", "Hello {{doc.name}}"); + + RenderTemplateRequest request = new RenderTemplateRequest().setTemplate(template); + + JSONObject json = new JSONObject(request.toString()); + + assertThat(json.has("template"), is(true)); + assertThat(json.has("input"), is(false)); + assertThat( + json.getJSONObject("template").getString("kind"), + is(equalTo("inlineDocumentTemplate"))); + assertThat( + json.getJSONObject("template").getString("inline"), + is(equalTo("Hello {{doc.name}}"))); + } + + @Test + void gettersAndSetters() { + Map template = new HashMap<>(); + template.put("kind", "inlineDocumentTemplate"); + + Map input = new HashMap<>(); + input.put("kind", "inlineDocument"); + + RenderTemplateRequest request = + RenderTemplateRequest.builder().template(template).input(input).build(); + + assertThat(request.getTemplate(), is(equalTo(template))); + assertThat(request.getInput(), is(equalTo(input))); + } +} From 320259985fe5cb750ed9ccbea0291ce64f879b25 Mon Sep 17 00:00:00 2001 From: Movindu Jayathilake Date: Tue, 30 Jun 2026 14:06:52 +0530 Subject: [PATCH 2/3] Address render template review comments Adds request serialization coverage and response deserialization coverage, and updates RenderTemplateResult for safer JSON mapping. --- .../sdk/model/RenderTemplateResult.java | 6 +- .../sdk/RenderTemplateRequestTest.java | 62 +++++++++++++++++++ .../sdk/model/RenderTemplateResultTest.java | 22 +++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java diff --git a/src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java b/src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java index 35b147f8..4d521a06 100644 --- a/src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java +++ b/src/main/java/com/meilisearch/sdk/model/RenderTemplateResult.java @@ -1,13 +1,15 @@ package com.meilisearch.sdk.model; import lombok.Getter; +import lombok.Setter; import lombok.ToString; @Getter +@Setter @ToString public class RenderTemplateResult { - Object template; - Object rendered; + private Object template; + private Object rendered; public RenderTemplateResult() {} } diff --git a/src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java b/src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java index e5c440b5..73399d53 100644 --- a/src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java +++ b/src/test/java/com/meilisearch/sdk/RenderTemplateRequestTest.java @@ -4,6 +4,11 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import com.meilisearch.sdk.http.request.BasicRequest; +import com.meilisearch.sdk.http.request.HttpMethod; +import com.meilisearch.sdk.http.request.HttpRequest; +import com.meilisearch.sdk.json.GsonJsonHandler; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.json.JSONObject; @@ -69,6 +74,63 @@ void toStringWithoutInput() { is(equalTo("Hello {{doc.name}}"))); } + @Test + void requestSerializerWithTemplateAndInput() { + Map template = new HashMap<>(); + template.put("kind", "inlineDocumentTemplate"); + template.put("inline", "Product {{doc.name}} is a {{doc.color}} {{doc.category}}."); + + Map product = new HashMap<>(); + product.put("name", "Nike Air Max"); + product.put("color", "Black"); + product.put("category", "Shoes"); + + Map input = new HashMap<>(); + input.put("kind", "inlineDocument"); + input.put("inline", product); + + RenderTemplateRequest request = + new RenderTemplateRequest().setTemplate(template).setInput(input); + + BasicRequest basicRequest = new BasicRequest(new GsonJsonHandler()); + HttpRequest httpRequest = + basicRequest.create( + HttpMethod.POST, "/render-template", Collections.emptyMap(), request); + + JSONObject json = new JSONObject(httpRequest.getContent()); + + assertThat(httpRequest.getMethod(), is(equalTo(HttpMethod.POST))); + assertThat(httpRequest.getPath(), is(equalTo("/render-template"))); + assertThat( + json.getJSONObject("template").getString("kind"), + is(equalTo("inlineDocumentTemplate"))); + assertThat(json.getJSONObject("input").getString("kind"), is(equalTo("inlineDocument"))); + assertThat( + json.getJSONObject("input").getJSONObject("inline").getString("name"), + is(equalTo("Nike Air Max"))); + } + + @Test + void requestSerializerWithoutInput() { + Map template = new HashMap<>(); + template.put("kind", "inlineDocumentTemplate"); + template.put("inline", "Hello {{doc.name}}"); + + RenderTemplateRequest request = new RenderTemplateRequest().setTemplate(template); + + BasicRequest basicRequest = new BasicRequest(new GsonJsonHandler()); + HttpRequest httpRequest = + basicRequest.create( + HttpMethod.POST, "/render-template", Collections.emptyMap(), request); + + JSONObject json = new JSONObject(httpRequest.getContent()); + + assertThat(httpRequest.getMethod(), is(equalTo(HttpMethod.POST))); + assertThat(httpRequest.getPath(), is(equalTo("/render-template"))); + assertThat(json.has("template"), is(true)); + assertThat(json.has("input"), is(false)); + } + @Test void gettersAndSetters() { Map template = new HashMap<>(); diff --git a/src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java b/src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java new file mode 100644 index 00000000..74599b60 --- /dev/null +++ b/src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java @@ -0,0 +1,22 @@ +package com.meilisearch.sdk.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +import com.meilisearch.sdk.json.JacksonJsonHandler; +import org.junit.jupiter.api.Test; + +class RenderTemplateResultTest { + @Test + void deserializesWithJacksonJsonHandler() throws Exception { + RenderTemplateResult result = + new JacksonJsonHandler() + .decode( + "{\"template\":\"Hello {{doc.name}}\",\"rendered\":\"Hello Movindu\"}", + RenderTemplateResult.class); + + assertThat(result.getTemplate(), is(equalTo("Hello {{doc.name}}"))); + assertThat(result.getRendered(), is(equalTo("Hello Movindu"))); + } +} From a44e5c1594052f39ebcc4508e4b69d3d466d29dd Mon Sep 17 00:00:00 2001 From: Movindu Jayathilake Date: Tue, 30 Jun 2026 16:35:38 +0530 Subject: [PATCH 3/3] Add structured render template result test --- .../sdk/model/RenderTemplateResultTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java b/src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java index 74599b60..f9ccde0a 100644 --- a/src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java +++ b/src/test/java/com/meilisearch/sdk/model/RenderTemplateResultTest.java @@ -5,6 +5,8 @@ import static org.hamcrest.Matchers.is; import com.meilisearch.sdk.json.JacksonJsonHandler; +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Test; class RenderTemplateResultTest { @@ -19,4 +21,28 @@ void deserializesWithJacksonJsonHandler() throws Exception { assertThat(result.getTemplate(), is(equalTo("Hello {{doc.name}}"))); assertThat(result.getRendered(), is(equalTo("Hello Movindu"))); } + + @Test + void deserializesStructuredRenderedValueWithJacksonJsonHandler() throws Exception { + RenderTemplateResult result = + new JacksonJsonHandler() + .decode( + "{" + + "\"template\":\"{{ doc }}\"," + + "\"rendered\":{" + + "\"title\":\"Ariel\"," + + "\"tags\":[\"movie\",\"classic\"]" + + "}" + + "}", + RenderTemplateResult.class); + + assertThat(result.getTemplate(), is(equalTo("{{ doc }}"))); + + Map rendered = (Map) result.getRendered(); + assertThat(rendered.get("title"), is(equalTo("Ariel"))); + + List tags = (List) rendered.get("tags"); + assertThat(tags.get(0), is(equalTo("movie"))); + assertThat(tags.get(1), is(equalTo("classic"))); + } }