Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -843,3 +843,6 @@ update_localized_attribute_settings_1: |-
);
reset_localized_attribute_settings_1: |-
client.index("INDEX_NAME").resetLocalizedAttributesSettings();
export_post_1: |-
ExportRequest request = ExportRequest.builder().url("http://anothermeiliinstance:7070").build();
client.export(request);
12 changes: 12 additions & 0 deletions src/main/java/com/meilisearch/sdk/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,18 @@ public TaskInfo createSnapshot() throws MeilisearchException {
return config.httpClient.post("/snapshots", "", TaskInfo.class);
}

/**
* Triggers the export of documents between Meilisearch instances.
*
* @param request Export request parameters
* @return Meilisearch API response as TaskInfo
* @throws MeilisearchException if an error occurs
* @see <a href="https://www.meilisearch.com/docs/reference/api/export">API specification</a>
*/
public TaskInfo export(ExportRequest request) throws MeilisearchException {
return config.httpClient.post("/export", request, TaskInfo.class);
}

/**
* Gets the status and availability of a Meilisearch instance
*
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/com/meilisearch/sdk/ExportIndexFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.meilisearch.sdk;

import lombok.*;
import org.json.JSONObject;

@Builder
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@NoArgsConstructor(access = AccessLevel.PACKAGE)
@Getter
@Setter
public class ExportIndexFilter {
private String filter;
@Builder.Default private boolean overrideSettings = false;

/**
* Method that returns the JSON String of the ExportIndexFilter
*
* @return JSON String of the ExportIndexFilter query
*/
@Override
public String toString() {
JSONObject jsonObject = new JSONObject();
if (this.filter != null) {
jsonObject.put("filter", this.filter);
}
if (this.overrideSettings) {
jsonObject.put("overrideSettings", true);
}

return jsonObject.toString();
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/meilisearch/sdk/ExportRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.meilisearch.sdk;

import java.util.Map;
import lombok.*;
import org.json.JSONObject;

@Builder
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@NoArgsConstructor(access = AccessLevel.PACKAGE)
@Getter
@Setter
public class ExportRequest {
private String url;
private String apiKey;
private String payloadSize;
private Map<String, ExportIndexFilter> indexes;

/**
* Method that returns the JSON String of the ExportRequest
*
* @return JSON String of the ExportRequest query
*/
@Override
public String toString() {
JSONObject jsonObject =
new JSONObject()
.put("url", this.url)
.putOpt("apiKey", this.apiKey)
.putOpt("payloadSize", this.payloadSize)
.putOpt("indexes", this.indexes);
return jsonObject.toString();
Comment on lines +25 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Do not expose secrets in toString(); redact apiKey.

toString() is frequently logged. Emitting a raw API key is a security risk.

Apply this diff to redact the key:

-        JSONObject jsonObject =
-                new JSONObject()
-                        .put("url", this.url)
-                        .putOpt("apiKey", this.apiKey)
-                        .putOpt("payloadSize", this.payloadSize)
-                        .putOpt("indexes", this.indexes);
+        String redactedApiKey = (this.apiKey == null) ? null : "REDACTED";
+        JSONObject jsonObject =
+                new JSONObject()
+                        .put("url", this.url)
+                        .putOpt("apiKey", redactedApiKey)
+                        .putOpt("payloadSize", this.payloadSize)
+                        .putOpt("indexes", this.indexes);
         return jsonObject.toString();

If you need an unredacted JSON string for tests, consider adding a dedicated method like toJson(boolean redactSecrets) and update tests accordingly. I can draft that change and the test updates if you want.

🤖 Prompt for AI Agents
In src/main/java/com/meilisearch/sdk/ExportRequest.java around lines 25 to 31,
the toString() method currently includes the raw apiKey, which is a security
risk. Modify the method to redact the apiKey value by replacing it with a
placeholder like "REDACTED" before returning the JSON string. Alternatively,
create a new method such as toJson(boolean redactSecrets) that conditionally
redacts the apiKey, and update tests to use this method accordingly.

}
}
20 changes: 20 additions & 0 deletions src/test/java/com/meilisearch/integration/ClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
import com.google.gson.*;
import com.meilisearch.integration.classes.AbstractIT;
import com.meilisearch.integration.classes.TestData;
import com.meilisearch.sdk.ExportIndexFilter;
import com.meilisearch.sdk.ExportRequest;
import com.meilisearch.sdk.Index;
import com.meilisearch.sdk.exceptions.MeilisearchApiException;
import com.meilisearch.sdk.exceptions.MeilisearchException;
import com.meilisearch.sdk.model.*;
import com.meilisearch.sdk.utils.Movie;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.*;

@Tag("integration")
Expand Down Expand Up @@ -308,6 +312,22 @@ public void testCreateSnapshot() throws Exception {
assertThat(snapshot.getType(), is(equalTo("snapshotCreation")));
}

/** Test call to initiate export */
@Test
public void testExport() throws Exception {
Map<String, ExportIndexFilter> indexes = new HashMap<>();
indexes.put("*", ExportIndexFilter.builder().filter("genres = action").build());

ExportRequest payload =
ExportRequest.builder().url(getMeilisearchHost()).indexes(indexes).build();
TaskInfo task = client.export(payload);
client.waitForTask(task.getTaskUid());
Task exportTask = client.getTask(task.getTaskUid());

assertThat(task.getStatus(), is(equalTo(TaskStatus.ENQUEUED)));
assertThat(exportTask.getType(), is(equalTo("export")));
}

/**
* Test the exclusion of transient fields.
*
Expand Down
100 changes: 100 additions & 0 deletions src/test/java/com/meilisearch/sdk/ExportRequestTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.meilisearch.sdk;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;

import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;

public class ExportRequestTest {

@Test
void toStringSimpleExportIndexFilter() {
ExportIndexFilter filter = ExportIndexFilter.builder().build();
String expected = "{\"overrideSettings\":false}";
assertThat(filter.toString(), is(equalTo(expected)));
assertThat(filter.getFilter(), is(nullValue()));
assertThat(filter.isOverrideSettings(), is(false));
}

@Test
void toStringExportIndexFilterWithOverride() {
ExportIndexFilter filter = ExportIndexFilter.builder().overrideSettings(true).build();
String expected = "{\"overrideSettings\":true}";
assertThat(filter.toString(), is(equalTo(expected)));
assertThat(filter.isOverrideSettings(), is(true));
}

@Test
void toStringExportIndexFilterWithFilter() {
ExportIndexFilter filter = ExportIndexFilter.builder().filter("status = 'active'").build();
String expected = "{\"filter\":\"status = 'active'\",\"overrideSettings\":false}";
assertThat(filter.toString(), is(equalTo(expected)));
assertThat(filter.getFilter(), is(equalTo("status = 'active'")));
}

@Test
void toStringSimpleExportRequest() {
ExportRequest request =
ExportRequest.builder().url("http://localhost:7711").payloadSize("123 MiB").build();
JSONObject json = new JSONObject(request.toString());
assertThat(json.getString("url"), is(equalTo("http://localhost:7711")));
assertThat(json.getString("payloadSize"), is(equalTo("123 MiB")));
assertThat(json.isNull("apiKey"), is(true));
assertThat(json.isNull("indexes"), is(true));
}

@Test
void toStringExportRequestWithIndexes() {
Map<String, ExportIndexFilter> indexes = new HashMap<>();
indexes.put("*", ExportIndexFilter.builder().overrideSettings(true).build());

ExportRequest request =
ExportRequest.builder()
.url("http://localhost:7711")
.payloadSize("123 MiB")
.indexes(indexes)
.build();

String expected =
"{\"url\":\"http://localhost:7711\",\"payloadSize\":\"123 MiB\",\"indexes\":{\"*\":{\"overrideSettings\":true}}}";
JSONObject expectedJson = new JSONObject(expected);
JSONObject json = new JSONObject(request.toString());

assertThat(expectedJson.toString(), is(json.toString()));

assertThat(json.getString("url"), is(equalTo("http://localhost:7711")));
assertThat(json.getString("payloadSize"), is(equalTo("123 MiB")));
assertThat(json.isNull("apiKey"), is(true));
JSONObject indexesJson = json.getJSONObject("indexes");
JSONObject starIndex = indexesJson.getJSONObject("*");
assertThat(starIndex.isNull("filter"), is(true));
assertThat(starIndex.getBoolean("overrideSettings"), is(true));
}

@Test
void gettersExportRequest() {
Map<String, ExportIndexFilter> indexes = new HashMap<>();
indexes.put(
"myindex",
ExportIndexFilter.builder().filter("id > 10").overrideSettings(false).build());

ExportRequest request =
ExportRequest.builder()
.url("http://localhost:7711")
.apiKey("mykey")
.payloadSize("50 MiB")
.indexes(indexes)
.build();

assertThat(request.getUrl(), is(equalTo("http://localhost:7711")));
assertThat(request.getApiKey(), is(equalTo("mykey")));
assertThat(request.getPayloadSize(), is(equalTo("50 MiB")));
assertThat(request.getIndexes().get("myindex").getFilter(), is(equalTo("id > 10")));
assertThat(request.getIndexes().get("myindex").isOverrideSettings(), is(false));
}
}
Loading