Преглед изворни кода

Add Create Repository High Level REST API (#30501)

This commit adds Create Repository, the associated docs and tests
for the high level REST API client. A few small changes to the
PutRepository Request and Response went into the commit as well.
Michael Basnight пре 7 година
родитељ
комит
b94bc70aee

+ 21 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

@@ -30,6 +30,7 @@ import org.apache.http.entity.ContentType;
 import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.action.DocWriteRequest;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
 import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
 import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
 import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
@@ -679,6 +680,19 @@ final class RequestConverters {
         return request;
     }
 
+    static Request createRepository(PutRepositoryRequest putRepositoryRequest) throws IOException {
+        String endpoint = new EndpointBuilder().addPathPart("_snapshot").addPathPart(putRepositoryRequest.name()).build();
+        Request request = new Request(HttpPut.METHOD_NAME, endpoint);
+
+        Params parameters = new Params(request);
+        parameters.withMasterTimeout(putRepositoryRequest.masterNodeTimeout());
+        parameters.withTimeout(putRepositoryRequest.timeout());
+        parameters.withVerify(putRepositoryRequest.verify());
+
+        request.setEntity(createEntity(putRepositoryRequest, REQUEST_BODY_CONTENT_TYPE));
+        return request;
+    }
+
     static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) throws IOException {
         String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addPathPart(putIndexTemplateRequest.name()).build();
         Request request = new Request(HttpPut.METHOD_NAME, endpoint);
@@ -917,6 +931,13 @@ final class RequestConverters {
             }
             return this;
         }
+
+        Params withVerify(boolean verify) {
+            if (verify) {
+                return putParam("verify", Boolean.TRUE.toString());
+            }
+            return this;
+        }
     }
 
     /**

+ 25 - 2
client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java

@@ -23,8 +23,8 @@ import org.apache.http.Header;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse;
-import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
-import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
 
 import java.io.IOException;
 
@@ -67,4 +67,27 @@ public final class SnapshotClient {
         restHighLevelClient.performRequestAsyncAndParseEntity(getRepositoriesRequest, RequestConverters::getRepositories,
             GetRepositoriesResponse::fromXContent, listener, emptySet(), headers);
     }
+
+    /**
+     * Creates a snapshot repository.
+     * <p>
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
+     * API on elastic.co</a>
+     */
+    public PutRepositoryResponse createRepository(PutRepositoryRequest putRepositoryRequest, Header... headers) throws IOException {
+        return restHighLevelClient.performRequestAndParseEntity(putRepositoryRequest, RequestConverters::createRepository,
+            PutRepositoryResponse::fromXContent, emptySet(), headers);
+    }
+
+    /**
+     * Asynchronously creates a snapshot repository.
+     * <p>
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
+     * API on elastic.co</a>
+     */
+    public void createRepositoryAsync(PutRepositoryRequest putRepositoryRequest,
+                                      ActionListener<PutRepositoryResponse> listener, Header... headers) {
+        restHighLevelClient.performRequestAsyncAndParseEntity(putRepositoryRequest, RequestConverters::createRepository,
+            PutRepositoryResponse::fromXContent, listener, emptySet(), headers);
+    }
 }

+ 26 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java

@@ -30,6 +30,7 @@ import org.apache.http.util.EntityUtils;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.DocWriteRequest;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
 import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
 import org.elasticsearch.action.admin.indices.alias.Alias;
 import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
@@ -77,9 +78,11 @@ import org.elasticsearch.common.CheckedFunction;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.io.PathUtils;
 import org.elasticsearch.common.io.Streams;
 import org.elasticsearch.common.lucene.uid.Versions;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.unit.ByteSizeUnit;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -94,6 +97,7 @@ import org.elasticsearch.index.rankeval.RankEvalRequest;
 import org.elasticsearch.index.rankeval.RankEvalSpec;
 import org.elasticsearch.index.rankeval.RatedRequest;
 import org.elasticsearch.index.rankeval.RestRankEvalAction;
+import org.elasticsearch.repositories.fs.FsRepository;
 import org.elasticsearch.rest.action.search.RestSearchAction;
 import org.elasticsearch.script.ScriptType;
 import org.elasticsearch.script.mustache.SearchTemplateRequest;
@@ -112,6 +116,7 @@ import org.elasticsearch.test.RandomObjects;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -1480,6 +1485,27 @@ public class RequestConvertersTests extends ESTestCase {
         assertThat(expectedParams, equalTo(request.getParameters()));
     }
 
+    public void testCreateRepository() throws IOException {
+        String repository = "repo";
+        String endpoint = "/_snapshot/" + repository;
+        Path repositoryLocation = PathUtils.get(".");
+        PutRepositoryRequest putRepositoryRequest = new PutRepositoryRequest(repository);
+        putRepositoryRequest.type(FsRepository.TYPE);
+        putRepositoryRequest.verify(randomBoolean());
+
+        putRepositoryRequest.settings(
+            Settings.builder()
+            .put(FsRepository.LOCATION_SETTING.getKey(), repositoryLocation)
+            .put(FsRepository.COMPRESS_SETTING.getKey(), randomBoolean())
+            .put(FsRepository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(100, 1000), ByteSizeUnit.BYTES)
+            .build());
+
+        Request request = RequestConverters.createRepository(putRepositoryRequest);
+        assertThat(endpoint, equalTo(request.getEndpoint()));
+        assertThat(HttpPut.METHOD_NAME, equalTo(request.getMethod()));
+        assertToXContentBody(putRepositoryRequest, request.getEntity());
+    }
+
     public void testPutTemplateRequest() throws Exception {
         Map<String, String> names = new HashMap<>();
         names.put("log", "log");

+ 29 - 29
client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java

@@ -19,56 +19,56 @@
 
 package org.elasticsearch.client;
 
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
 import org.elasticsearch.ElasticsearchException;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
+import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.repositories.fs.FsRepository;
 import org.elasticsearch.rest.RestStatus;
 
 import java.io.IOException;
-import java.util.Collections;
 
 import static org.hamcrest.Matchers.equalTo;
 
 public class SnapshotIT extends ESRestHighLevelClientTestCase {
 
-    public void testModulesGetRepositoriesUsingParams() throws IOException {
-        String repository = "test";
-        String repositorySettings = "{\"type\":\"fs\", \"settings\":{\"location\": \".\"}}";
-        highLevelClient().getLowLevelClient().performRequest("put", "_snapshot/" + repository, Collections.emptyMap(),
-            new StringEntity(repositorySettings, ContentType.APPLICATION_JSON));
-
-        highLevelClient().getLowLevelClient().performRequest("put", "_snapshot/" + repository + "_other", Collections.emptyMap(),
-            new StringEntity(repositorySettings, ContentType.APPLICATION_JSON));
+    private PutRepositoryResponse createTestRepository(String repository, String type, String settings) throws IOException {
+        PutRepositoryRequest request = new PutRepositoryRequest(repository);
+        request.settings(settings, XContentType.JSON);
+        request.type(type);
+        return execute(request, highLevelClient().snapshot()::createRepository,
+            highLevelClient().snapshot()::createRepositoryAsync);
 
-        {
-            GetRepositoriesRequest request = new GetRepositoriesRequest();
-            request.repositories(new String[]{repository});
-            GetRepositoriesResponse response = execute(request, highLevelClient().snapshot()::getRepositories,
-                highLevelClient().snapshot()::getRepositoriesAsync);
-            assertThat(1, equalTo(response.repositories().size()));
-        }
-        {
-            GetRepositoriesRequest request = new GetRepositoriesRequest();
-            GetRepositoriesResponse response = execute(request, highLevelClient().snapshot()::getRepositories,
-                highLevelClient().snapshot()::getRepositoriesAsync);
-            assertThat(2, equalTo(response.repositories().size()));
-        }
     }
 
-    public void testModulesGetDefaultRepositories() throws IOException {
-        String repositorySettings = "{\"type\":\"fs\", \"settings\":{\"location\": \".\"}}";
-        GetRepositoriesRequest request = new GetRepositoriesRequest();
+    public void testCreateRepository() throws IOException {
+        PutRepositoryResponse response = createTestRepository("test", FsRepository.TYPE, "{\"location\": \".\"}");
+        assertTrue(response.isAcknowledged());
+    }
 
-        highLevelClient().getLowLevelClient().performRequest("put", "_snapshot/test", Collections.emptyMap(),
-        new StringEntity(repositorySettings, ContentType.APPLICATION_JSON));
+    public void testModulesGetRepositoriesUsingParams() throws IOException {
+        String testRepository = "test";
+        assertTrue(createTestRepository(testRepository, FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged());
+        assertTrue(createTestRepository("other", FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged());
 
+        GetRepositoriesRequest request = new GetRepositoriesRequest();
+        request.repositories(new String[]{testRepository});
         GetRepositoriesResponse response = execute(request, highLevelClient().snapshot()::getRepositories,
             highLevelClient().snapshot()::getRepositoriesAsync);
         assertThat(1, equalTo(response.repositories().size()));
     }
 
+    public void testModulesGetDefaultRepositories() throws IOException {
+        assertTrue(createTestRepository("other", FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged());
+        assertTrue(createTestRepository("test", FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged());
+
+        GetRepositoriesResponse response = execute(new GetRepositoriesRequest(), highLevelClient().snapshot()::getRepositories,
+            highLevelClient().snapshot()::getRepositoriesAsync);
+        assertThat(2, equalTo(response.repositories().size()));
+    }
+
     public void testModulesGetRepositoriesNonExistent() throws IOException {
         String repository = "doesnotexist";
         GetRepositoriesRequest request = new GetRepositoriesRequest(new String[]{repository});

+ 121 - 12
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java

@@ -19,20 +19,24 @@
 
 package org.elasticsearch.client.documentation;
 
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.LatchedActionListener;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest;
 import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
 import org.elasticsearch.client.ESRestHighLevelClientTestCase;
 import org.elasticsearch.client.RestHighLevelClient;
 import org.elasticsearch.cluster.metadata.RepositoryMetaData;
+import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.repositories.fs.FsRepository;
 
 import java.io.IOException;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -58,7 +62,114 @@ import static org.hamcrest.Matchers.equalTo;
  */
 public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase {
 
-    private static final String testRepository = "test_repository";
+    private static final String repositoryName = "test_repository";
+
+    public void testSnapshotCreateRepository() throws IOException {
+        RestHighLevelClient client = highLevelClient();
+
+        // tag::create-repository-request
+        PutRepositoryRequest request = new PutRepositoryRequest();
+        // end::create-repository-request
+
+        // tag::create-repository-create-settings
+        String locationKey = FsRepository.LOCATION_SETTING.getKey();
+        String locationValue = ".";
+        String compressKey = FsRepository.COMPRESS_SETTING.getKey();
+        boolean compressValue = true;
+
+        Settings settings = Settings.builder()
+            .put(locationKey, locationValue)
+            .put(compressKey, compressValue)
+            .build(); // <1>
+        // end::create-repository-create-settings
+
+        // tag::create-repository-request-repository-settings
+        request.settings(settings); // <1>
+        // end::create-repository-request-repository-settings
+
+        {
+            // tag::create-repository-settings-builder
+            Settings.Builder settingsBuilder = Settings.builder()
+                .put(locationKey, locationValue)
+                .put(compressKey, compressValue);
+            request.settings(settingsBuilder); // <1>
+            // end::create-repository-settings-builder
+        }
+        {
+            // tag::create-repository-settings-map
+            Map<String, Object> map = new HashMap<>();
+            map.put(locationKey, locationValue);
+            map.put(compressKey, compressValue);
+            request.settings(map); // <1>
+            // end::create-repository-settings-map
+        }
+        {
+            // tag::create-repository-settings-source
+            request.settings("{\"location\": \".\", \"compress\": \"true\"}",
+                XContentType.JSON); // <1>
+            // end::create-repository-settings-source
+        }
+
+        // tag::create-repository-request-name
+        request.name(repositoryName); // <1>
+        // end::create-repository-request-name
+        // tag::create-repository-request-type
+        request.type(FsRepository.TYPE); // <1>
+        // end::create-repository-request-type
+
+        // tag::create-repository-request-masterTimeout
+        request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
+        request.masterNodeTimeout("1m"); // <2>
+        // end::create-repository-request-masterTimeout
+        // tag::create-repository-request-timeout
+        request.timeout(TimeValue.timeValueMinutes(1)); // <1>
+        request.timeout("1m"); // <2>
+        // end::create-repository-request-timeout
+        // tag::create-repository-request-verify
+        request.verify(true); // <1>
+        // end::create-repository-request-verify
+
+        // tag::create-repository-execute
+        PutRepositoryResponse response = client.snapshot().createRepository(request);
+        // end::create-repository-execute
+
+        // tag::create-repository-response
+        boolean acknowledged = response.isAcknowledged(); // <1>
+        // end::create-repository-response
+        assertTrue(acknowledged);
+    }
+
+    public void testSnapshotCreateRepositoryAsync() throws InterruptedException {
+        RestHighLevelClient client = highLevelClient();
+        {
+            PutRepositoryRequest request = new PutRepositoryRequest(repositoryName);
+
+            // tag::create-repository-execute-listener
+            ActionListener<PutRepositoryResponse> listener =
+                new ActionListener<PutRepositoryResponse>() {
+                    @Override
+                    public void onResponse(PutRepositoryResponse putRepositoryResponse) {
+                        // <1>
+                    }
+
+                    @Override
+                    public void onFailure(Exception e) {
+                        // <2>
+                    }
+                };
+            // end::create-repository-execute-listener
+
+            // Replace the empty listener by a blocking listener in test
+            final CountDownLatch latch = new CountDownLatch(1);
+            listener = new LatchedActionListener<>(listener, latch);
+
+            // tag::create-repository-execute-async
+            client.snapshot().createRepositoryAsync(request, listener); // <1>
+            // end::create-repository-execute-async
+
+            assertTrue(latch.await(30L, TimeUnit.SECONDS));
+        }
+    }
 
     public void testSnapshotGetRepository() throws IOException {
         RestHighLevelClient client = highLevelClient();
@@ -70,7 +181,7 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
         // end::get-repository-request
 
         // tag::get-repository-request-repositories
-        String [] repositories = new String[] { testRepository };
+        String [] repositories = new String[] {repositoryName};
         request.repositories(repositories); // <1>
         // end::get-repository-request-repositories
         // tag::get-repository-request-local
@@ -89,7 +200,7 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
         List<RepositoryMetaData> repositoryMetaDataResponse = response.repositories();
         // end::get-repository-response
         assertThat(1, equalTo(repositoryMetaDataResponse.size()));
-        assertThat(testRepository, equalTo(repositoryMetaDataResponse.get(0).name()));
+        assertThat(repositoryName, equalTo(repositoryMetaDataResponse.get(0).name()));
     }
 
     public void testSnapshotGetRepositoryAsync() throws InterruptedException {
@@ -122,14 +233,12 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
 
             assertTrue(latch.await(30L, TimeUnit.SECONDS));
         }
-
     }
 
     private void createTestRepositories() throws IOException {
-        RestHighLevelClient client = highLevelClient();
-        String repositorySettings = "{\"type\":\"fs\", \"settings\":{\"location\": \".\"}}";
-        highLevelClient().getLowLevelClient().performRequest("put", "_snapshot/" + testRepository, Collections.emptyMap(),
-            new StringEntity(repositorySettings, ContentType.APPLICATION_JSON));
-
+        PutRepositoryRequest request = new PutRepositoryRequest(repositoryName);
+        request.type(FsRepository.TYPE);
+        request.settings("{\"location\": \".\"}", XContentType.JSON);
+        assertTrue(highLevelClient().snapshot().createRepository(request).isAcknowledged());
     }
 }

+ 139 - 0
docs/java-rest/high-level/snapshot/create_repository.asciidoc

@@ -0,0 +1,139 @@
+[[java-rest-high-snapshot-create-repository]]
+=== Snapshot Create RepositoryAPI
+
+The Snapshot Create RepositoryAPI allows to register a snapshot repository.
+
+[[java-rest-high-snapshot-create-repository-request]]
+==== Snapshot Create RepositoryRequest
+
+A `PutRepositoryRequest`:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request]
+--------------------------------------------------
+
+==== Repository Settings
+Settings requirements will differ based on the repository backend chosen.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request-repository-settings]
+--------------------------------------------------
+<1> Sets the repository settings
+
+==== Providing the Settings
+The settings to be applied can be provided in different ways:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-create-settings]
+--------------------------------------------------
+<1> Settings provided as `Settings`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-settings-builder]
+--------------------------------------------------
+<1> Settings provided as `Settings.Builder`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-settings-source]
+--------------------------------------------------
+<1> Settings provided as `String`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-settings-map]
+--------------------------------------------------
+<1> Settings provided as a `Map`
+
+==== Required Arguments
+The following arguments must be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request-name]
+--------------------------------------------------
+<1> The name of the repository
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request-type]
+--------------------------------------------------
+<1> The type of the repository
+
+==== Optional Arguments
+The following arguments can optionally be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request-timeout]
+--------------------------------------------------
+<1> Timeout to wait for the all the nodes to acknowledge the settings were applied
+as a `TimeValue`
+<2> Timeout to wait for the all the nodes to acknowledge the settings were applied
+as a `String`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request-masterTimeout]
+--------------------------------------------------
+<1> Timeout to connect to the master node as a `TimeValue`
+<2> Timeout to connect to the master node as a `String`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request-verify]
+--------------------------------------------------
+<1> Verify after creation as a `Boolean`
+
+[[java-rest-high-snapshot-create-repository-sync]]
+==== Synchronous Execution
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-execute]
+--------------------------------------------------
+
+[[java-rest-high-snapshot-create-repository-async]]
+==== Asynchronous Execution
+
+The asynchronous execution of a repository put settings requires both the
+`PutRepositoryRequest` instance and an `ActionListener` instance to be
+passed to the asynchronous method:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-execute-async]
+--------------------------------------------------
+<1> The `PutRepositoryRequest` to execute and the `ActionListener`
+to use when the execution completes
+
+The asynchronous method does not block and returns immediately. Once it is
+completed the `ActionListener` is called back using the `onResponse` method
+if the execution successfully completed or using the `onFailure` method if
+it failed.
+
+A typical listener for `PutRepositoryResponse` looks like:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-execute-listener]
+--------------------------------------------------
+<1> Called when the execution is successfully completed. The response is
+provided as an argument
+<2> Called in case of a failure. The raised exception is provided as an argument
+
+[[java-rest-high-snapshot-create-repository-response]]
+==== Snapshot Create RepositoryResponse
+
+The returned `PutRepositoryResponse` allows to retrieve information about the
+executed operation as follows:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-response]
+--------------------------------------------------
+<1> Indicates the node has acknowledged the request

+ 2 - 1
docs/java-rest/high-level/supported-apis.asciidoc

@@ -113,4 +113,5 @@ The Java High Level REST Client supports the following Snapshot APIs:
 
 * <<java-rest-high-snapshot-get-repository>>
 
-include::snapshot/get_repository.asciidoc[]
+include::snapshot/get_repository.asciidoc[]
+include::snapshot/create_repository.asciidoc[]

+ 17 - 1
server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryRequest.java

@@ -26,6 +26,7 @@ import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentType;
@@ -44,7 +45,7 @@ import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
  * Registers a repository with given name, type and settings. If the repository with the same name already
  * exists in the cluster, the new repository will replace the existing repository.
  */
-public class PutRepositoryRequest extends AcknowledgedRequest<PutRepositoryRequest> {
+public class PutRepositoryRequest extends AcknowledgedRequest<PutRepositoryRequest> implements ToXContentObject {
 
     private String name;
 
@@ -232,4 +233,19 @@ public class PutRepositoryRequest extends AcknowledgedRequest<PutRepositoryReque
         writeSettingsToStream(settings, out);
         out.writeBoolean(verify);
     }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        builder.field("name", name);
+        builder.field("type", type);
+
+        builder.startObject("settings");
+        settings.toXContent(builder, params);
+        builder.endObject();
+
+        builder.field("verify", verify);
+        builder.endObject();
+        return builder;
+    }
 }

+ 12 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryResponse.java

@@ -22,6 +22,8 @@ package org.elasticsearch.action.admin.cluster.repositories.put;
 import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.XContentParser;
 
 import java.io.IOException;
 
@@ -30,6 +32,13 @@ import java.io.IOException;
  */
 public class PutRepositoryResponse extends AcknowledgedResponse {
 
+    private static final ConstructingObjectParser<PutRepositoryResponse, Void> PARSER = new ConstructingObjectParser<>("put_repository",
+        true, args -> new PutRepositoryResponse((boolean) args[0]));
+
+    static {
+        declareAcknowledgedField(PARSER);
+    }
+
     PutRepositoryResponse() {
     }
 
@@ -49,4 +58,7 @@ public class PutRepositoryResponse extends AcknowledgedResponse {
         writeAcknowledged(out);
     }
 
+    public static PutRepositoryResponse fromXContent(XContentParser parser) {
+        return PARSER.apply(parser, null);
+    }
 }

+ 72 - 0
server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryRequestTests.java

@@ -0,0 +1,72 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.action.admin.cluster.repositories.put;
+
+import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentHelper;
+import org.elasticsearch.repositories.fs.FsRepository;
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.hamcrest.Matchers.equalTo;
+
+public class PutRepositoryRequestTests extends ESTestCase {
+
+    public void testCreateRepositoryToXContent() throws IOException {
+        Map<String, String> mapParams = new HashMap<>();
+        PutRepositoryRequest request = new PutRepositoryRequest();
+        String repoName = "test";
+        request.name(repoName);
+        mapParams.put("name", repoName);
+        Boolean verify = randomBoolean();
+        request.verify(verify);
+        mapParams.put("verify", verify.toString());
+        String type = FsRepository.TYPE;
+        request.type(type);
+        mapParams.put("type", type);
+
+        Boolean addSettings = randomBoolean();
+        if (addSettings) {
+            request.settings(Settings.builder().put(FsRepository.LOCATION_SETTING.getKey(), ".").build());
+        }
+
+        XContentBuilder builder = jsonBuilder();
+        request.toXContent(builder, new ToXContent.MapParams(mapParams));
+        builder.flush();
+
+        Map<String, Object> outputMap = XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2();
+
+        assertThat(outputMap.get("name"), equalTo(request.name()));
+        assertThat(outputMap.get("verify"), equalTo(request.verify()));
+        assertThat(outputMap.get("type"), equalTo(request.type()));
+        Map settings = (Map) outputMap.get("settings");
+        if (addSettings) {
+            assertThat(settings.get(FsRepository.LOCATION_SETTING.getKey()), equalTo("."));
+        } else {
+            assertTrue(((Map) outputMap.get("settings")).isEmpty());
+        }
+    }
+}

+ 48 - 0
server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryResponseTests.java

@@ -0,0 +1,48 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.action.admin.cluster.repositories.put;
+
+import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.common.xcontent.json.JsonXContent;
+import org.elasticsearch.test.AbstractStreamableXContentTestCase;
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static org.elasticsearch.test.ESTestCase.randomBoolean;
+import static org.hamcrest.Matchers.equalTo;
+
+public class PutRepositoryResponseTests extends AbstractStreamableXContentTestCase<PutRepositoryResponse> {
+
+    @Override
+    protected PutRepositoryResponse doParseInstance(XContentParser parser) throws IOException {
+        return PutRepositoryResponse.fromXContent(parser);
+    }
+
+    @Override
+    protected PutRepositoryResponse createBlankInstance() {
+        return new PutRepositoryResponse();
+    }
+
+    @Override
+    protected PutRepositoryResponse createTestInstance() {
+        return new PutRepositoryResponse(randomBoolean());
+    }
+}