Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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().setUrl("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
28 changes: 28 additions & 0 deletions src/main/java/com/meilisearch/sdk/ExportIndexFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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 ExportRequest
*
* @return JSON String of the ExportRequest query
*/
@Override
public String toString() {
JSONObject jsonObject =
new JSONObject()
.putOpt("filter", this.filter)
.putOpt("overrideSettings", this.overrideSettings);
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 snapshot = client.getTask(task.getTaskUid());

assertThat(task.getStatus(), is(equalTo(TaskStatus.ENQUEUED)));
assertThat(snapshot.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));
}
}